From ae72f66677ce2606f9083199edf268348964f17b Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Tue, 6 Jan 2026 08:13:01 +0100 Subject: [PATCH 01/31] Adds breadcrumbs to search resutls for Topic and News --- src/onegov/agency/layout.py | 4 ++-- src/onegov/agency/views/page.py | 4 ++-- src/onegov/org/app.py | 1 + src/onegov/org/directives.py | 28 +++++++++++++++++++++++ src/onegov/org/layout.py | 2 +- src/onegov/org/request.py | 11 +++++++++ src/onegov/org/views/editor.py | 16 ++++++------- src/onegov/org/views/page.py | 6 ++--- src/onegov/town6/layout.py | 9 +++++--- src/onegov/town6/templates/macros.pt | 11 +++++++-- src/onegov/town6/theme/styles/search.scss | 4 ++-- tests/onegov/org/test_layout.py | 12 +++++----- 12 files changed, 79 insertions(+), 29 deletions(-) diff --git a/src/onegov/agency/layout.py b/src/onegov/agency/layout.py index 44d21f605a..4db7ba3c20 100644 --- a/src/onegov/agency/layout.py +++ b/src/onegov/agency/layout.py @@ -17,7 +17,7 @@ from onegov.org import _ from onegov.org.layout import AdjacencyListLayout from onegov.org.layout import DefaultLayout -from onegov.org.layout import PageLayout as OrgPageLayout +from onegov.org.layout import TopicLayout as OrgPageLayout from onegov.org.layout import PersonCollectionLayout from onegov.org.layout import PersonLayout as OrgPersonLayout @@ -33,7 +33,7 @@ from onegov.org.elements import Trait -class PageLayout(OrgPageLayout): +class TopicLayout(OrgPageLayout): @cached_property def sidebar_links(self) -> None: # type:ignore[override] diff --git a/src/onegov/agency/views/page.py b/src/onegov/agency/views/page.py index 74b4f28a78..27efd0f9bf 100644 --- a/src/onegov/agency/views/page.py +++ b/src/onegov/agency/views/page.py @@ -1,7 +1,7 @@ from __future__ import annotations from onegov.agency import AgencyApp -from onegov.agency.layout import PageLayout +from onegov.agency.layout import TopicLayout from onegov.core.security import Public from onegov.org.models import Topic from onegov.org.views.page import view_topic as view_topic_base @@ -19,5 +19,5 @@ def view_topic( self: Topic, request: AgencyRequest ) -> RenderData | Response: - layout = PageLayout(self, request) + layout = TopicLayout(self, request) return view_topic_base(self, request, layout) diff --git a/src/onegov/org/app.py b/src/onegov/org/app.py index d554d4b5a7..dab9e8ab3c 100644 --- a/src/onegov/org/app.py +++ b/src/onegov/org/app.py @@ -69,6 +69,7 @@ class OrgApp(Framework, LibresIntegration, SearchApp, MapboxApp, event_search_widget = directive(directives.EventSearchWidgetAction) settings_view = directive(directives.SettingsView) boardlet = directive(directives.Boardlet) + layout = directive(directives.Layout) #: cronjob settings send_ticket_statistics = True diff --git a/src/onegov/org/directives.py b/src/onegov/org/directives.py index 93b4f2aa6f..331d408619 100644 --- a/src/onegov/org/directives.py +++ b/src/onegov/org/directives.py @@ -293,3 +293,31 @@ def perform( # type:ignore[override] 'order': self.order, 'icon': self.icon, } + + +class Layout(Action): + """ + Registers a layout for a model. This is used to show breadcrumbs + for search results. + """ + + config = { + 'layout_registry': dict + } + + def __init__(self, model: type) -> None: + self.model = model + + def identifier( # type:ignore[override] + self, + layout_registry: dict[type, Layout] + ) -> str: + return str(self.model) + + def perform( # type:ignore[override] + self, + layout: Layout, + layout_registry: dict[type, Layout] + ) -> None: + print('*** tschupre register layout for model', self.model, layout) + layout_registry[self.model] = layout diff --git a/src/onegov/org/layout.py b/src/onegov/org/layout.py index 14aed066b9..557d54d8e8 100644 --- a/src/onegov/org/layout.py +++ b/src/onegov/org/layout.py @@ -1047,7 +1047,7 @@ def breadcrumbs(self) -> list[Link]: return bc -class PageLayout(AdjacencyListLayout): +class TopicLayout(AdjacencyListLayout): @cached_property def og_image_source(self) -> str | None: diff --git a/src/onegov/org/request.py b/src/onegov/org/request.py index 18b5996364..330336a520 100644 --- a/src/onegov/org/request.py +++ b/src/onegov/org/request.py @@ -6,6 +6,7 @@ from onegov.core.request import CoreRequest from onegov.core.security import Private from onegov.core.utils import normalize_for_url +from onegov.org.layout import DefaultLayout, Layout from onegov.org.models import News, TANAccessCollection, Topic from onegov.page import Page, PageCollection from onegov.user import User @@ -275,3 +276,13 @@ def visit_topics( ) return result + + def get_layout(self, model: object) -> Layout | DefaultLayout: + """ + Get the registered layout for a model instance. + """ + layout_registry = self.app.config.layout_registry + model_type = model if isinstance(model, type) else type(model) + layout_class = layout_registry.get(model_type, DefaultLayout) + print('*** tschupre returning', layout_class) + return layout_class(model, self) diff --git a/src/onegov/org/views/editor.py b/src/onegov/org/views/editor.py index 04b186c425..29fcde2b08 100644 --- a/src/onegov/org/views/editor.py +++ b/src/onegov/org/views/editor.py @@ -8,7 +8,7 @@ from onegov.core.security import Private from onegov.org import _, OrgApp from onegov.org.forms.page import MovePageForm, PageUrlForm, PageForm -from onegov.org.layout import EditorLayout, PageLayout +from onegov.org.layout import EditorLayout, TopicLayout from onegov.org.management import PageNameChange from onegov.org.models import Clipboard, Editor from onegov.org.models.organisation import Organisation @@ -57,7 +57,7 @@ def handle_page_form( request: OrgRequest, form: Form, # FIXME: This is really bad, they should all use the same layout - layout: EditorLayout | PageLayout | None = None + layout: EditorLayout | TopicLayout | None = None ) -> RenderData | Response: if self.action == 'new': return handle_new_page(self, request, form, layout=layout) @@ -85,7 +85,7 @@ def handle_new_page( request: OrgRequest, form: Form, src: object | None = None, - layout: EditorLayout | PageLayout | None = None + layout: EditorLayout | TopicLayout | None = None ) -> RenderData | Response: assert self.page is not None page = cast('Topic | News', self.page) @@ -129,7 +129,7 @@ def handle_new_root_page( self: Editor, request: OrgRequest, form: Form, - layout: EditorLayout | PageLayout | None = None + layout: EditorLayout | TopicLayout | None = None ) -> RenderData | Response: site_title = _('New Topic') @@ -171,7 +171,7 @@ def handle_edit_page( self: Editor, request: OrgRequest, form: Form, - layout: EditorLayout | PageLayout | None = None + layout: EditorLayout | TopicLayout | None = None ) -> RenderData | Response: assert self.page is not None site_title = self.page.trait_messages[self.trait]['edit_page_title'] @@ -217,11 +217,11 @@ def handle_move_page( self: Editor, request: OrgRequest, form: Form, - layout: EditorLayout | PageLayout | None = None + layout: EditorLayout | TopicLayout | None = None ) -> RenderData | Response: assert self.page is not None - layout = layout or PageLayout(self.page, request) + layout = layout or TopicLayout(self.page, request) site_title = self.page.trait_messages[self.trait]['move_page_title'] layout.site_title = site_title # type:ignore[union-attr] @@ -244,7 +244,7 @@ def handle_change_page_url( self: Editor, request: OrgRequest, form: Form, - layout: EditorLayout | PageLayout | None = None + layout: EditorLayout | TopicLayout | None = None ) -> RenderData | Response: if not request.is_admin: diff --git a/src/onegov/org/views/page.py b/src/onegov/org/views/page.py index 99807ce7d3..a11f18c4bf 100644 --- a/src/onegov/org/views/page.py +++ b/src/onegov/org/views/page.py @@ -13,7 +13,7 @@ from onegov.org import _, OrgApp from onegov.org.elements import Link from onegov.org.homepage_widgets import get_lead -from onegov.org.layout import PageLayout, NewsLayout +from onegov.org.layout import TopicLayout, NewsLayout from onegov.org.models import News, NewsCollection, Topic from onegov.org.models.editor import Editor from onegov.page import PageCollection @@ -50,7 +50,7 @@ def delete_page(self: Topic | News, request: OrgRequest) -> None: def view_topic( self: Topic, request: OrgRequest, - layout: PageLayout | None = None + layout: TopicLayout | None = None ) -> RenderData | Response: assert self.trait in {'link', 'page', 'iframe'} @@ -63,7 +63,7 @@ def view_topic( if self.trait == 'link': return morepath.redirect(self.content['url']) - layout = layout or PageLayout(self, request) + layout = layout or TopicLayout(self, request) if self.photo_album_id: request.include('photoswipe') diff --git a/src/onegov/town6/layout.py b/src/onegov/town6/layout.py index f16bc9e222..9bb043c134 100644 --- a/src/onegov/town6/layout.py +++ b/src/onegov/town6/layout.py @@ -42,7 +42,7 @@ MessageCollectionLayout as OrgMessageCollectionLayout, NewsLayout as OrgNewsLayout, NewsletterLayout as OrgNewsletterLayout, - PageLayout as OrgPageLayout, + TopicLayout as OrgTopicLayout, PaymentCollectionLayout as OrgPaymentCollectionLayout, PaymentProviderLayout as OrgPaymentProviderLayout, PersonCollectionLayout as OrgPersonCollectionLayout, @@ -69,7 +69,7 @@ UserGroupLayout as OrgUserGroupLayout, UserGroupCollectionLayout as OrgUserGroupCollectionLayout, UserManagementLayout as OrgUserManagementLayout) -from onegov.org.models import MeetingCollection +from onegov.org.models import MeetingCollection, Topic, News from onegov.org.models import PageMove from onegov.org.models import PoliticalBusinessCollection from onegov.org.models import RISCommissionCollection @@ -81,6 +81,7 @@ from onegov.stepsequence.extension import StepsLayoutExtension from onegov.town6 import _ from onegov.town6.theme import user_options +from onegov.town6 import TownApp from typing import Any, NamedTuple, TypeVar, TYPE_CHECKING @@ -272,7 +273,8 @@ class SettingsLayout(OrgSettingsLayout, DefaultLayout): request: TownRequest -class PageLayout(OrgPageLayout, AdjacencyListLayout): +@TownApp.layout(model=Topic) +class PageLayout(OrgTopicLayout, AdjacencyListLayout): app: TownApp request: TownRequest @@ -284,6 +286,7 @@ def contact_html(self) -> str: ) +@TownApp.layout(model=News) class NewsLayout(OrgNewsLayout, AdjacencyListLayout): app: TownApp diff --git a/src/onegov/town6/templates/macros.pt b/src/onegov/town6/templates/macros.pt index b8fcb8b5ae..9ea968e2c7 100644 --- a/src/onegov/town6/templates/macros.pt +++ b/src/onegov/town6/templates/macros.pt @@ -2056,10 +2056,17 @@
${result.title}
-

+

${result.lead[:160]} -

+
+
+ +
diff --git a/src/onegov/town6/theme/styles/search.scss b/src/onegov/town6/theme/styles/search.scss index 43b176a02a..408d7e6e2d 100644 --- a/src/onegov/town6/theme/styles/search.scss +++ b/src/onegov/town6/theme/styles/search.scss @@ -146,9 +146,9 @@ color: $jumbo; } -.search-results em { +.search-preview em { color: $red !important; - font-style: initial; + font-style: inherit; font-weight: bold; text-decoration: underline; } \ No newline at end of file diff --git a/tests/onegov/org/test_layout.py b/tests/onegov/org/test_layout.py index 7eb4c75079..d6ad89ea11 100644 --- a/tests/onegov/org/test_layout.py +++ b/tests/onegov/org/test_layout.py @@ -12,7 +12,7 @@ from onegov.core.elements import Link from onegov.org.layout import ( DefaultLayout, - PageLayout + TopicLayout ) from onegov.page import Page from webtest import TestApp as Client @@ -92,7 +92,7 @@ def test_page_layout_sidebar(session: Session) -> None: ) session.add(page) - layout = PageLayout(page, MockRequest()) # type: ignore[arg-type] + layout = TopicLayout(page, MockRequest()) # type: ignore[arg-type] layout.homepage_url = 'http://nohost' assert len(layout.sidebar_links) == 1 @@ -104,7 +104,7 @@ def test_page_layout_sidebar(session: Session) -> None: ), ) - layout = PageLayout(page.children[0], MockRequest()) # type: ignore[arg-type] + layout = TopicLayout(page.children[0], MockRequest()) # type: ignore[arg-type] assert len(layout.sidebar_links) == 1 assert layout.sidebar_links[0].title == 'Ma' @@ -116,7 +116,7 @@ def test_page_layout_sidebar(session: Session) -> None: ), ) - layout = PageLayout(page.children[0].children[0], MockRequest()) # type: ignore[arg-type] + layout = TopicLayout(page.children[0].children[0], MockRequest()) # type: ignore[arg-type] assert len(layout.sidebar_links) == 1 assert layout.sidebar_links[0].title == 'Ada' @@ -143,7 +143,7 @@ def test_page_layout_breadcrumbs(session: Session) -> None: ) session.add(page) - layout = PageLayout(page, MockRequest()) # type: ignore[arg-type] + layout = TopicLayout(page, MockRequest()) # type: ignore[arg-type] layout.homepage_url = 'http://nohost' links = layout.breadcrumbs @@ -153,7 +153,7 @@ def test_page_layout_breadcrumbs(session: Session) -> None: assert links[1].text == 'Grandma' assert links[1].attrs['href'] == 'grandma' - layout = PageLayout(page.children[0], MockRequest()) # type: ignore[arg-type] + layout = TopicLayout(page.children[0], MockRequest()) # type: ignore[arg-type] layout.homepage_url = 'http://nohost' links = layout.breadcrumbs From 0ece4694cd9e007cf0465d7b94f613b8cdd64d68 Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Tue, 6 Jan 2026 14:20:25 +0100 Subject: [PATCH 02/31] Adds breadcrumbs to search resutls for Person and User --- src/onegov/town6/layout.py | 6 ++++++ src/onegov/town6/templates/macros.pt | 25 ++++++++++++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/onegov/town6/layout.py b/src/onegov/town6/layout.py index 9bb043c134..45a3d31b45 100644 --- a/src/onegov/town6/layout.py +++ b/src/onegov/town6/layout.py @@ -77,14 +77,18 @@ from onegov.org.models import RISParliamentaryGroupCollection from onegov.org.models.directory import ExtendedDirectoryEntryCollection from onegov.page import PageCollection +from onegov.people import Person from onegov.stepsequence import step_sequences from onegov.stepsequence.extension import StepsLayoutExtension +from onegov.user import User from onegov.town6 import _ from onegov.town6.theme import user_options from onegov.town6 import TownApp from typing import Any, NamedTuple, TypeVar, TYPE_CHECKING + + if TYPE_CHECKING: from collections.abc import Iterator from onegov.event import Event @@ -424,6 +428,7 @@ class PersonCollectionLayout(OrgPersonCollectionLayout, DefaultLayout): request: TownRequest +@TownApp.layout(model=Person) class PersonLayout(OrgPersonLayout, DefaultLayout): app: TownApp @@ -716,6 +721,7 @@ class UserManagementLayout(OrgUserManagementLayout, DefaultLayout): request: TownRequest +@TownApp.layout(model=User) class UserLayout(OrgUserLayout, DefaultLayout): app: TownApp diff --git a/src/onegov/town6/templates/macros.pt b/src/onegov/town6/templates/macros.pt index 9ea968e2c7..0c8d32d7e0 100644 --- a/src/onegov/town6/templates/macros.pt +++ b/src/onegov/town6/templates/macros.pt @@ -1933,16 +1933,28 @@ + + + + + +
${result.title}
-

+

${translate(result.role.capitalize())} -

+
+
+ +
-
@@ -2033,8 +2045,11 @@
${result.title}
-

${result.function}

-

People

+
${result.function}
+
People
+
+ +
From 6cc9cb1c5ea241093fdedd9e6356413c315021c9 Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Tue, 6 Jan 2026 14:43:29 +0100 Subject: [PATCH 03/31] Adds breadcrumbs to search resutls for Files and highlights it on the files view --- src/onegov/org/layout.py | 17 ++++++++++++++++- src/onegov/town6/layout.py | 18 +++++++++++++++--- src/onegov/town6/templates/macros.pt | 20 +++++++++----------- src/onegov/town6/theme/styles/files.scss | 10 ++++++++++ 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/onegov/org/layout.py b/src/onegov/org/layout.py index 557d54d8e8..8b86f91b76 100644 --- a/src/onegov/org/layout.py +++ b/src/onegov/org/layout.py @@ -36,7 +36,7 @@ from onegov.org import _ from onegov.org import utils from onegov.org.exports.base import OrgExport -from onegov.org.models import CitizenDashboard +from onegov.org.models import CitizenDashboard, GeneralFile from onegov.org.models import ExportCollection, Editor from onegov.org.models import GeneralFileCollection from onegov.org.models import ImageFile @@ -289,6 +289,21 @@ def get_link(locale: str) -> str: for locale in sorted(self.app.locales) ] + @cached_property + def files_url(self) -> str: + """ Returns the url to the files view. """ + url = self.request.link( + GeneralFileCollection(self.request.session) + ) + return self.csrf_protected_url(url) + + def files_url_with_anchor(self, file: GeneralFile | None) -> str: + """ Returns the url to the files view including anchor. """ + if file is None: + return self.files_url + + return self.files_url + f'#{file.name}' + @cached_property def file_upload_url(self) -> str: """ Returns the url to the file upload action. """ diff --git a/src/onegov/town6/layout.py b/src/onegov/town6/layout.py index 45a3d31b45..2fc98ed90e 100644 --- a/src/onegov/town6/layout.py +++ b/src/onegov/town6/layout.py @@ -69,7 +69,7 @@ UserGroupLayout as OrgUserGroupLayout, UserGroupCollectionLayout as OrgUserGroupCollectionLayout, UserManagementLayout as OrgUserManagementLayout) -from onegov.org.models import MeetingCollection, Topic, News +from onegov.org.models import MeetingCollection, Topic, News, GeneralFile from onegov.org.models import PageMove from onegov.org.models import PoliticalBusinessCollection from onegov.org.models import RISCommissionCollection @@ -88,9 +88,8 @@ from typing import Any, NamedTuple, TypeVar, TYPE_CHECKING - if TYPE_CHECKING: - from collections.abc import Iterator + from collections.abc import Iterator, Sequence from onegov.event import Event from onegov.form import FormDefinition, FormSubmission from onegov.form.models.definition import SurveyDefinition @@ -941,6 +940,7 @@ class DashboardLayout(OrgDashboardLayout, DefaultLayout): request: TownRequest +@TownApp.layout(model=GeneralFile) class GeneralFileCollectionLayout(DefaultLayout): def __init__(self, model: Any, request: TownRequest) -> None: @@ -953,6 +953,18 @@ def __init__(self, model: Any, request: TownRequest) -> None: request.include('upload') request.include('prompt') + @cached_property + def breadcrumbs(self) -> Sequence[Link]: + name = self.model.name[:40] + if len(name) == 40: + name = name[:37] + '...' + + return [ + Link(_('Homepage'), self.homepage_url), + Link(_('Files'), self.files_url), + Link(name, self.files_url_with_anchor(self.model)), + ] + class ImageFileCollectionLayout(DefaultLayout): diff --git a/src/onegov/town6/templates/macros.pt b/src/onegov/town6/templates/macros.pt index 0c8d32d7e0..8cdaad36f1 100644 --- a/src/onegov/town6/templates/macros.pt +++ b/src/onegov/town6/templates/macros.pt @@ -1962,7 +1962,7 @@
-

+

Has a digital seal, @@ -1975,11 +1975,13 @@ ${result.stats.get('pages', 0)} pages -

+
+
+ +
-
@@ -2072,15 +2074,11 @@
- ${result.lead[:160]} + ${result.lead[:260]}
-
- +
+
@@ -2819,7 +2817,7 @@ file_upload file.upload_date|file.created; file_publish_end file.publish_end_date|None; "> - + ${file.name} diff --git a/src/onegov/town6/theme/styles/files.scss b/src/onegov/town6/theme/styles/files.scss index d532437175..af57b43bc8 100644 --- a/src/onegov/town6/theme/styles/files.scss +++ b/src/onegov/town6/theme/styles/files.scss @@ -122,6 +122,16 @@ section#content .files { a[id^="button"] { cursor: pointer; } + + &:target, + &:has(:target) { + background-color: rgba($primary-color, 0.2); + transition: background-color 0.5s ease; + } + } + + tr.file-info[id] { + scroll-margin-top: calc(100vh / 2.5); // 40% } .file-info + tr { From 713a69765e2a6c47c50ff6a5cf987ddb9219b85c Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Thu, 8 Jan 2026 08:54:42 +0100 Subject: [PATCH 04/31] Fix town import in org --- src/onegov/org/exports/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/onegov/org/exports/base.py b/src/onegov/org/exports/base.py index e2446bcc52..b76fc3fb87 100644 --- a/src/onegov/org/exports/base.py +++ b/src/onegov/org/exports/base.py @@ -1,7 +1,7 @@ from __future__ import annotations from onegov.org.models import Export -from onegov.town6 import _ +from onegov.org import _ from typing import Any, TYPE_CHECKING From 1d0b0098bb4db39f36d1cc7e616b73d9fcf852ef Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Thu, 8 Jan 2026 09:00:58 +0100 Subject: [PATCH 05/31] Adds breadcrumbs to search resutls for forms --- src/onegov/org/layout.py | 15 +++++++++++++++ src/onegov/town6/layout.py | 20 ++++++++++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/onegov/org/layout.py b/src/onegov/org/layout.py index 8b86f91b76..8cd757a8c5 100644 --- a/src/onegov/org/layout.py +++ b/src/onegov/org/layout.py @@ -1344,6 +1344,21 @@ def editbar_links(self) -> list[Link | LinkGroup] | None: return None +class FormDefinitionLayout(DefaultLayout): + + @property + def forms_url(self) -> str: + return self.request.class_link(FormCollection) + + @cached_property + def breadcrumbs(self) -> list[Link]: + return [ + Link(_('Homepage'), self.homepage_url), + Link(_('Forms'), self.forms_url), + Link(self.model.title, self.request.link(self.model)) + ] + + class SurveySubmissionWindowLayout(DefaultLayout): @cached_property def breadcrumbs(self) -> list[Link]: diff --git a/src/onegov/town6/layout.py b/src/onegov/town6/layout.py index 2fc98ed90e..30613be729 100644 --- a/src/onegov/town6/layout.py +++ b/src/onegov/town6/layout.py @@ -10,7 +10,6 @@ from onegov.chat.models import Chat from onegov.directory import DirectoryCollection from onegov.event import OccurrenceCollection -from onegov.form import FormCollection from onegov.org.elements import QrCodeLink, IFrameLink from onegov.org.layout import ( Layout as OrgLayout, @@ -30,6 +29,7 @@ ExternalLinkLayout as OrgExternalLinkLayout, FindYourSpotLayout as OrgFindYourSpotLayout, FormCollectionLayout as OrgFormCollectionLayout, + FormDefinitionLayout as OrgFormDefinitionLayout, SurveyCollectionLayout as OrgSurveyCollectionLayout, FormEditorLayout as OrgFormEditorLayout, FormSubmissionLayout as OrgFormSubmissionLayout, @@ -69,12 +69,16 @@ UserGroupLayout as OrgUserGroupLayout, UserGroupCollectionLayout as OrgUserGroupCollectionLayout, UserManagementLayout as OrgUserManagementLayout) -from onegov.org.models import MeetingCollection, Topic, News, GeneralFile +from onegov.org.models import CustomFormDefinition +from onegov.org.models import GeneralFile +from onegov.org.models import MeetingCollection +from onegov.org.models import News from onegov.org.models import PageMove from onegov.org.models import PoliticalBusinessCollection from onegov.org.models import RISCommissionCollection from onegov.org.models import RISParliamentarianCollection from onegov.org.models import RISParliamentaryGroupCollection +from onegov.org.models import Topic from onegov.org.models.directory import ExtendedDirectoryEntryCollection from onegov.page import PageCollection from onegov.people import Person @@ -91,7 +95,8 @@ if TYPE_CHECKING: from collections.abc import Iterator, Sequence from onegov.event import Event - from onegov.form import FormDefinition, FormSubmission + from onegov.form import FormSubmission + from onegov.form import FormDefinition from onegov.form.models.definition import SurveyDefinition from onegov.form.models.submission import SurveySubmission from onegov.org.models import ExtendedDirectoryEntry @@ -403,9 +408,12 @@ class FormCollectionLayout(OrgFormCollectionLayout, DefaultLayout): app: TownApp request: TownRequest - @property - def forms_url(self) -> str: - return self.request.class_link(FormCollection) + +@TownApp.layout(model=CustomFormDefinition) +class FormDefinitionLayout(OrgFormDefinitionLayout, DefaultLayout): + + app: TownApp + request: TownRequest class SurveySubmissionWindowLayout(OrgSurveySubmissionWindowLayout, From bd1b9d290349120b07e0a0f7561757bcabe4d79f Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Thu, 8 Jan 2026 09:08:19 +0100 Subject: [PATCH 06/31] Adds breadcrumbs to search resutls for image sets --- src/onegov/town6/layout.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/onegov/town6/layout.py b/src/onegov/town6/layout.py index 30613be729..c8eeeb1822 100644 --- a/src/onegov/town6/layout.py +++ b/src/onegov/town6/layout.py @@ -69,7 +69,7 @@ UserGroupLayout as OrgUserGroupLayout, UserGroupCollectionLayout as OrgUserGroupCollectionLayout, UserManagementLayout as OrgUserManagementLayout) -from onegov.org.models import CustomFormDefinition +from onegov.org.models import CustomFormDefinition, ImageSet from onegov.org.models import GeneralFile from onegov.org.models import MeetingCollection from onegov.org.models import News @@ -716,6 +716,7 @@ class ImageSetCollectionLayout(OrgImageSetCollectionLayout, DefaultLayout): request: TownRequest +@TownApp.layout(model=ImageSet) class ImageSetLayout(OrgImageSetLayout, DefaultLayout): app: TownApp From d32a3d6d3aed6a341da78882968eb3aa9b905544 Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Thu, 8 Jan 2026 11:04:43 +0100 Subject: [PATCH 07/31] Adds breadcrumbs to search resutls for resources --- src/onegov/org/request.py | 12 ++++++++++-- src/onegov/town6/layout.py | 9 +++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/onegov/org/request.py b/src/onegov/org/request.py index 330336a520..f9b1309a29 100644 --- a/src/onegov/org/request.py +++ b/src/onegov/org/request.py @@ -283,6 +283,14 @@ def get_layout(self, model: object) -> Layout | DefaultLayout: """ layout_registry = self.app.config.layout_registry model_type = model if isinstance(model, type) else type(model) - layout_class = layout_registry.get(model_type, DefaultLayout) - print('*** tschupre returning', layout_class) + + layout_class = None + for cls in model_type.mro(): + layout_class = layout_registry.get(cls) + if layout_class: + break + + if layout_class is None: + layout_class = DefaultLayout + return layout_class(model, self) diff --git a/src/onegov/town6/layout.py b/src/onegov/town6/layout.py index c8eeeb1822..a714bf0050 100644 --- a/src/onegov/town6/layout.py +++ b/src/onegov/town6/layout.py @@ -69,7 +69,8 @@ UserGroupLayout as OrgUserGroupLayout, UserGroupCollectionLayout as OrgUserGroupCollectionLayout, UserManagementLayout as OrgUserManagementLayout) -from onegov.org.models import CustomFormDefinition, ImageSet +from onegov.form import FormDefinition +from onegov.org.models import ImageSet from onegov.org.models import GeneralFile from onegov.org.models import MeetingCollection from onegov.org.models import News @@ -82,6 +83,7 @@ from onegov.org.models.directory import ExtendedDirectoryEntryCollection from onegov.page import PageCollection from onegov.people import Person +from onegov.reservation import Resource from onegov.stepsequence import step_sequences from onegov.stepsequence.extension import StepsLayoutExtension from onegov.user import User @@ -96,13 +98,11 @@ from collections.abc import Iterator, Sequence from onegov.event import Event from onegov.form import FormSubmission - from onegov.form import FormDefinition from onegov.form.models.definition import SurveyDefinition from onegov.form.models.submission import SurveySubmission from onegov.org.models import ExtendedDirectoryEntry from onegov.org.request import PageMeta from onegov.page import Page - from onegov.reservation import Resource from onegov.ticket import Ticket from onegov.town6.app import TownApp from onegov.town6.request import TownRequest @@ -409,7 +409,7 @@ class FormCollectionLayout(OrgFormCollectionLayout, DefaultLayout): request: TownRequest -@TownApp.layout(model=CustomFormDefinition) +@TownApp.layout(model=FormDefinition) class FormDefinitionLayout(OrgFormDefinitionLayout, DefaultLayout): app: TownApp @@ -566,6 +566,7 @@ class ResourceRecipientsFormLayout( request: TownRequest +@TownApp.layout(model=Resource) class ResourceLayout(OrgResourceLayout, DefaultLayout): app: TownApp From 7e0f149f3f398df762f0948692a18232e9029985 Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Thu, 8 Jan 2026 12:09:22 +0100 Subject: [PATCH 08/31] Adds breadcrumbs to search resutls for Event/Occurrence --- src/onegov/town6/layout.py | 6 ++++-- src/onegov/town6/templates/macros.pt | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/onegov/town6/layout.py b/src/onegov/town6/layout.py index a714bf0050..9d2f2a8be3 100644 --- a/src/onegov/town6/layout.py +++ b/src/onegov/town6/layout.py @@ -9,7 +9,8 @@ from onegov.chat.collections import ChatCollection from onegov.chat.models import Chat from onegov.directory import DirectoryCollection -from onegov.event import OccurrenceCollection +from onegov.event import Event +from onegov.event import OccurrenceCollection, Occurrence from onegov.org.elements import QrCodeLink, IFrameLink from onegov.org.layout import ( Layout as OrgLayout, @@ -96,7 +97,6 @@ if TYPE_CHECKING: from collections.abc import Iterator, Sequence - from onegov.event import Event from onegov.form import FormSubmission from onegov.form.models.definition import SurveyDefinition from onegov.form.models.submission import SurveySubmission @@ -647,6 +647,7 @@ def editbar_links(self) -> list[Link | LinkGroup]: return links +@TownApp.layout(model=Occurrence) class OccurrenceLayout(OrgOccurrenceLayout, DefaultLayout): app: TownApp @@ -677,6 +678,7 @@ def editbar_links(self) -> list[Link | LinkGroup]: cls_before='EventLayout', cls_after='TicketChatMessageLayout' ) +@TownApp.layout(model=Event) class EventLayout(StepsLayoutExtension, OrgEventLayout, DefaultLayout): app: TownApp diff --git a/src/onegov/town6/templates/macros.pt b/src/onegov/town6/templates/macros.pt index 8cdaad36f1..2303939ef1 100644 --- a/src/onegov/town6/templates/macros.pt +++ b/src/onegov/town6/templates/macros.pt @@ -2040,6 +2040,9 @@
+
+ +
From 8ecbaff2188cdfe1703133d7d75db3e3986fa657 Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Thu, 8 Jan 2026 12:10:20 +0100 Subject: [PATCH 09/31] Remove print statement --- src/onegov/org/directives.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/onegov/org/directives.py b/src/onegov/org/directives.py index 331d408619..b41090fd6a 100644 --- a/src/onegov/org/directives.py +++ b/src/onegov/org/directives.py @@ -319,5 +319,4 @@ def perform( # type:ignore[override] layout: Layout, layout_registry: dict[type, Layout] ) -> None: - print('*** tschupre register layout for model', self.model, layout) layout_registry[self.model] = layout From 502a4cc64ae81d1b763889a0981b2a6db4c712ec Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Thu, 8 Jan 2026 12:31:40 +0100 Subject: [PATCH 10/31] Adds breadcrumbs to search resutls for Ticket --- src/onegov/org/layout.py | 3 ++- src/onegov/town6/layout.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/onegov/org/layout.py b/src/onegov/org/layout.py index 8cd757a8c5..b360b78caf 100644 --- a/src/onegov/org/layout.py +++ b/src/onegov/org/layout.py @@ -1791,7 +1791,8 @@ def breadcrumbs(self) -> list[Link]: return [ Link(_('Homepage'), self.homepage_url), Link(_('Tickets'), get_current_tickets_url(self.request)), - Link(self.model.number, '#') + Link(self.model.number, self.request.link( + TicketCollection(self.request.session).by_id(self.model.id))) ] @cached_property diff --git a/src/onegov/town6/layout.py b/src/onegov/town6/layout.py index 9d2f2a8be3..a7df47e424 100644 --- a/src/onegov/town6/layout.py +++ b/src/onegov/town6/layout.py @@ -87,6 +87,7 @@ from onegov.reservation import Resource from onegov.stepsequence import step_sequences from onegov.stepsequence.extension import StepsLayoutExtension +from onegov.ticket import Ticket from onegov.user import User from onegov.town6 import _ from onegov.town6.theme import user_options @@ -103,7 +104,6 @@ from onegov.org.models import ExtendedDirectoryEntry from onegov.org.request import PageMeta from onegov.page import Page - from onegov.ticket import Ticket from onegov.town6.app import TownApp from onegov.town6.request import TownRequest from typing import TypeAlias @@ -454,6 +454,7 @@ class ArchivedTicketsLayout(OrgArchivedTicketsLayout, DefaultLayout): request: TownRequest +@TownApp.layout(model=Ticket) class TicketLayout(OrgTicketLayout, DefaultLayout): app: TownApp From 80facd9ea47e767aaab474bd55fcec9045d1c245 Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Thu, 8 Jan 2026 13:10:17 +0100 Subject: [PATCH 11/31] Adds breadcrumbs to search resutls for Directory and DirectoryEntry --- src/onegov/org/layout.py | 17 ++++++++++++++++- src/onegov/town6/layout.py | 11 ++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/onegov/org/layout.py b/src/onegov/org/layout.py index b360b78caf..ed85c240db 100644 --- a/src/onegov/org/layout.py +++ b/src/onegov/org/layout.py @@ -3458,7 +3458,9 @@ def og_description(self) -> str: def breadcrumbs(self) -> list[Link]: return [ Link(_('Homepage'), self.homepage_url), - Link(_('Directories'), '#') + Link(_('Directories'), self.request.class_link( + DirectoryCollection + )), ] @cached_property @@ -3482,6 +3484,19 @@ def editbar_links(self) -> list[Link | LinkGroup] | None: return None +class DirectoryLayout(DefaultLayout): + + @cached_property + def breadcrumbs(self) -> list[Link]: + return [ + Link(_('Homepage'), self.homepage_url), + Link(_('Directories'), self.request.class_link( + DirectoryCollection + )), + Link(_(self.model.title), self.request.link(self.model)) + ] + + class DirectoryEntryMixin: request: OrgRequest diff --git a/src/onegov/town6/layout.py b/src/onegov/town6/layout.py index a7df47e424..c6d8a3612e 100644 --- a/src/onegov/town6/layout.py +++ b/src/onegov/town6/layout.py @@ -8,7 +8,7 @@ from onegov.core.utils import append_query_param, to_html_ul from onegov.chat.collections import ChatCollection from onegov.chat.models import Chat -from onegov.directory import DirectoryCollection +from onegov.directory import DirectoryCollection, DirectoryEntry, Directory from onegov.event import Event from onegov.event import OccurrenceCollection, Occurrence from onegov.org.elements import QrCodeLink, IFrameLink @@ -22,6 +22,7 @@ ArchivedTicketsLayout as OrgArchivedTicketsLayout, DashboardLayout as OrgDashboardLayout, DirectoryCollectionLayout as OrgDirectoryCollectionLayout, + DirectoryLayout as OrgDirectoryLayout, DirectoryEntryCollectionLayout as OrgDirectoryEntryCollectionLayout, DirectoryEntryLayout as OrgDirectoryEntryLayout, EditorLayout as OrgEditorLayout, @@ -791,6 +792,13 @@ class DirectoryCollectionLayout(OrgDirectoryCollectionLayout, DefaultLayout): request: TownRequest +@TownApp.layout(model=Directory) +class DirectoryLayout(OrgDirectoryLayout, DefaultLayout): + + app: TownApp + request: TownRequest + + @step_sequences.registered_step( 1, _('Form'), cls_after='FormSubmissionLayout' ) @@ -918,6 +926,7 @@ def links() -> Iterator[Link | LinkGroup]: @step_sequences.registered_step(1, _('Form'), cls_after='FormSubmissionLayout') +@TownApp.layout(model=DirectoryEntry) class DirectoryEntryLayout( StepsLayoutExtension, OrgDirectoryEntryLayout, From ce6a0c949e2188b276211b488274e96ac465a920 Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Thu, 8 Jan 2026 17:50:01 +0100 Subject: [PATCH 12/31] Adds breadcrumbs to search resutls for RIS models --- src/onegov/town6/layout.py | 21 +++++++++++++++++++-- src/onegov/town6/templates/macros.pt | 3 +++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/onegov/town6/layout.py b/src/onegov/town6/layout.py index c6d8a3612e..23680025d7 100644 --- a/src/onegov/town6/layout.py +++ b/src/onegov/town6/layout.py @@ -72,18 +72,23 @@ UserGroupCollectionLayout as OrgUserGroupCollectionLayout, UserManagementLayout as OrgUserManagementLayout) from onegov.form import FormDefinition -from onegov.org.models import ImageSet from onegov.org.models import GeneralFile +from onegov.org.models import ImageSet +from onegov.org.models import Meeting from onegov.org.models import MeetingCollection from onegov.org.models import News from onegov.org.models import PageMove +from onegov.org.models import PoliticalBusiness from onegov.org.models import PoliticalBusinessCollection from onegov.org.models import RISCommissionCollection from onegov.org.models import RISParliamentarianCollection +from onegov.org.models import RISParliamentaryGroup from onegov.org.models import RISParliamentaryGroupCollection from onegov.org.models import Topic from onegov.org.models.directory import ExtendedDirectoryEntryCollection from onegov.page import PageCollection +from onegov.parliament.models import Commission +from onegov.parliament.models import Parliamentarian from onegov.people import Person from onegov.reservation import Resource from onegov.stepsequence import step_sequences @@ -1140,6 +1145,7 @@ def editbar_links(self) -> list[LinkGroup] | None: return None +@TownApp.layout(model=Meeting) class MeetingLayout(DefaultLayout): @cached_property @@ -1156,11 +1162,18 @@ def og_description(self) -> str: @cached_property def breadcrumbs(self) -> list[Link]: + title = ( + self.title + ' - ' + + self.format_date(self.model.start_datetime, 'date') + if self.model.start_datetime + else self.title + ) + return [ Link(_('Homepage'), self.homepage_url), Link(_('RIS Settings'), self.ris_overview_url), Link(_('Meetings'), self.request.class_link(MeetingCollection)), - Link(self.title, self.request.link(self.model)), + Link(title, self.request.link(self.model)), ] @cached_property @@ -1238,6 +1251,7 @@ def editbar_links(self) -> list[LinkGroup] | None: return None +@TownApp.layout(model=Parliamentarian) class RISParliamentarianLayout(DefaultLayout): @cached_property @@ -1419,6 +1433,7 @@ def editbar_links(self) -> list[LinkGroup] | None: return None +@TownApp.layout(model=RISParliamentaryGroup) class RISParliamentaryGroupLayout(DefaultLayout): @cached_property @@ -1554,6 +1569,7 @@ def editbar_links(self) -> list[LinkGroup] | None: return None +@TownApp.layout(model=Commission) class RISCommissionLayout(DefaultLayout): @cached_property @@ -1646,6 +1662,7 @@ def editbar_links(self) -> list[LinkGroup] | None: return None +@TownApp.layout(model=PoliticalBusiness) class PoliticalBusinessLayout(DefaultLayout): @cached_property diff --git a/src/onegov/town6/templates/macros.pt b/src/onegov/town6/templates/macros.pt index 2303939ef1..6a3081710c 100644 --- a/src/onegov/town6/templates/macros.pt +++ b/src/onegov/town6/templates/macros.pt @@ -1996,6 +1996,9 @@

+
+ +
From 7f971d570827beff86df8c5b512be14a9f8dbd1e Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Fri, 9 Jan 2026 09:27:21 +0100 Subject: [PATCH 13/31] Adjust tests --- tests/onegov/org/test_views_directory.py | 24 +++++++++++----------- tests/onegov/org/test_views_ticket.py | 2 +- tests/onegov/town6/test_views_directory.py | 12 +++++------ tests/onegov/winterthur/test_search.py | 4 ++-- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/onegov/org/test_views_directory.py b/tests/onegov/org/test_views_directory.py index ca36b061f2..b232ffa8d7 100644 --- a/tests/onegov/org/test_views_directory.py +++ b/tests/onegov/org/test_views_directory.py @@ -71,7 +71,7 @@ def create_directory( ) -> ExtendedResponse: client.login_admin() - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = title if lead: page.form['lead'] = lead @@ -305,7 +305,7 @@ def test_directory_change_requests(client: Client) -> None: client.login_admin() # create a directory that accepts change requests - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = "Playgrounds" page.form['structure'] = """ Name *= ___ @@ -372,7 +372,7 @@ def test_directory_submissions( client.login_admin() # create a directory does not accept submissions - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = "Points of Interest" page.form['structure'] = """ Name *= ___ @@ -550,7 +550,7 @@ def test_directory_visibility(client: Client) -> None: page = client.get('/directories') assert 'Noch keine Verzeichnisse' in page - page = page.click('Verzeichnis') + page = page.click('^Verzeichnis$') page.form['title'] = "Clubs" page.form['lead'] = 'The famous club directory' page.form['structure'] = """ @@ -631,7 +631,7 @@ def test_directory_visibility(client: Client) -> None: def test_markdown_in_directories(client: Client) -> None: client.login_admin() - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = "Clubs" page.form['structure'] = """ Name *= ___ @@ -807,7 +807,7 @@ def test_add_directory_entries_with_duplicate_names(client: Client) -> None: client.login_admin() duplicate_name = "duplicate" - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = "Playgrounds" page.form['structure'] = """ Name *= ___ @@ -833,7 +833,7 @@ def test_add_directory_entries_with_duplicate_names(client: Client) -> None: def test_directory_numbering(client: Client) -> None: client.login_admin() - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = "Trainers" page.form['structure'] = """ Name *= ___ @@ -917,7 +917,7 @@ def test_newline_in_directory_header(client: Client) -> None: client.login_admin() page = client.get('/directories') - page = page.click('Verzeichnis') + page = page.click('^Verzeichnis$') page.form['title'] = "Clubs" page.form['lead'] = 'this is a multiline\nlead' page.form['structure'] = """ @@ -938,7 +938,7 @@ def test_newline_in_directory_header(client: Client) -> None: def test_change_directory_url(client: Client) -> None: client.login_admin() - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = "Trainers" page.form['structure'] = """ Name *= ___ @@ -955,7 +955,7 @@ def test_change_directory_url(client: Client) -> None: assert sr.request.url.endswith('/sr') # now attempt to change url to a directory url which already exists - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = "Clubs" page.form['structure'] = """ Name *= ___ @@ -976,7 +976,7 @@ def test_directory_entry_subscription(client: Client) -> None: assert len(os.listdir(client.app.maildir)) == 0 - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = "Trainers" page.form['structure'] = """ Name *= ___ @@ -1032,7 +1032,7 @@ def test_create_directory_accordion_layout(client: Client) -> None: def create_directory(client: Client, title: str) -> ExtendedResponse: page = (client.get('/directories'). - click('Verzeichnis')) + click('^Verzeichnis$')) page.form['title'] = title page.form['structure'] = "Question *= ___\nAnswer *= ___" page.form['title_format'] = '[Question]' diff --git a/tests/onegov/org/test_views_ticket.py b/tests/onegov/org/test_views_ticket.py index 46522eede8..2431f8d0c8 100644 --- a/tests/onegov/org/test_views_ticket.py +++ b/tests/onegov/org/test_views_ticket.py @@ -310,7 +310,7 @@ def test_ticket_states_idempotent(client: Client) -> None: def test_ticket_states_directory_entry(client: Client) -> None: client.login_admin() - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = "Vereinsverzeichnis" page.form['structure'] = "Vereinsname *= ___" page.form['title_format'] = "[Vereinsname]" diff --git a/tests/onegov/town6/test_views_directory.py b/tests/onegov/town6/test_views_directory.py index 5045e18210..59adaaa988 100644 --- a/tests/onegov/town6/test_views_directory.py +++ b/tests/onegov/town6/test_views_directory.py @@ -14,7 +14,7 @@ def test_directory_prev_next(client: Client) -> None: client.login_admin() - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = "Trainers" page.form['structure'] = """ Name *= ___ @@ -64,7 +64,7 @@ def test_directory_prev_next(client: Client) -> None: def test_newline_in_directory_header(client: Client) -> None: client.login_admin() page = client.get('/directories') - page = page.click('Verzeichnis') + page = page.click('^Verzeichnis$') page.form['title'] = "Clubs" page.form['lead'] = 'this is a multiline\nlead' page.form['structure'] = """ @@ -85,7 +85,7 @@ def test_newline_in_directory_header(client: Client) -> None: def test_change_directory_url(client: Client) -> None: client.login_admin() - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = "Trainers" page.form['structure'] = """ Name *= ___ @@ -101,7 +101,7 @@ def test_change_directory_url(client: Client) -> None: assert sr.request.url.endswith('/sr') # now attempt to change url to a directory url which already exists - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = "Clubs" page.form['structure'] = """ Name *= ___ @@ -122,7 +122,7 @@ def test_directory_entry_subscription(client: Client) -> None: assert len(os.listdir(client.app.maildir)) == 0 - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = "Trainers" page.form['structure'] = """ Name *= ___ @@ -221,7 +221,7 @@ def create_directory( title: str, hide_labels: str ) -> ExtendedResponse: - page = (client.get('/directories').click('Verzeichnis')) + page = (client.get('/directories').click('^Verzeichnis$')) page.form['title'] = title + f' {index}' page.form['structure'] = "Question *= ___\nAnswer *= ___" page.form['title_format'] = '[Question]' diff --git a/tests/onegov/winterthur/test_search.py b/tests/onegov/winterthur/test_search.py index 9db51480b8..cdee4dc74a 100644 --- a/tests/onegov/winterthur/test_search.py +++ b/tests/onegov/winterthur/test_search.py @@ -16,7 +16,7 @@ def test_search_excluding_image(client_with_fts: Client[TestApp]) -> None: client.login_admin() # Create directory - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = 'Clubs' page.form['structure'] = """ Name *= ___ @@ -52,7 +52,7 @@ def test_search_malicious_term(client_with_fts: Client[TestApp]) -> None: client.login_admin() # Create directory - page = client.get('/directories').click('Verzeichnis') + page = client.get('/directories').click('^Verzeichnis$') page.form['title'] = 'Clubs' page.form['structure'] = """ Name *= ___ From 104a2484131510690936b71146b08940b4c11c2a Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Fri, 9 Jan 2026 09:43:41 +0100 Subject: [PATCH 14/31] Fix wrong model registration --- src/onegov/town6/layout.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/onegov/town6/layout.py b/src/onegov/town6/layout.py index 23680025d7..68e3b0050b 100644 --- a/src/onegov/town6/layout.py +++ b/src/onegov/town6/layout.py @@ -72,7 +72,7 @@ UserGroupCollectionLayout as OrgUserGroupCollectionLayout, UserManagementLayout as OrgUserManagementLayout) from onegov.form import FormDefinition -from onegov.org.models import GeneralFile +from onegov.org.models import GeneralFile, RISCommission, RISParliamentarian from onegov.org.models import ImageSet from onegov.org.models import Meeting from onegov.org.models import MeetingCollection @@ -87,8 +87,6 @@ from onegov.org.models import Topic from onegov.org.models.directory import ExtendedDirectoryEntryCollection from onegov.page import PageCollection -from onegov.parliament.models import Commission -from onegov.parliament.models import Parliamentarian from onegov.people import Person from onegov.reservation import Resource from onegov.stepsequence import step_sequences @@ -1251,7 +1249,7 @@ def editbar_links(self) -> list[LinkGroup] | None: return None -@TownApp.layout(model=Parliamentarian) +@TownApp.layout(model=RISParliamentarian) class RISParliamentarianLayout(DefaultLayout): @cached_property @@ -1569,7 +1567,7 @@ def editbar_links(self) -> list[LinkGroup] | None: return None -@TownApp.layout(model=Commission) +@TownApp.layout(model=RISCommission) class RISCommissionLayout(DefaultLayout): @cached_property From c74f7c334b7f3bc67b957548e4f5ec0166a24064 Mon Sep 17 00:00:00 2001 From: Reto Tschuppert Date: Fri, 9 Jan 2026 11:12:19 +0100 Subject: [PATCH 15/31] Move styles from template to stylesheet --- src/onegov/town6/templates/macros.pt | 4 ++-- src/onegov/town6/theme/styles/search.scss | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/onegov/town6/templates/macros.pt b/src/onegov/town6/templates/macros.pt index 6a3081710c..0b897294fc 100644 --- a/src/onegov/town6/templates/macros.pt +++ b/src/onegov/town6/templates/macros.pt @@ -1935,9 +1935,9 @@ -