feat: add reusable content fragments

This commit is contained in:
2026-08-07 14:53:34 +02:00
parent 6f8a70f864
commit 21ed6270a3
7 changed files with 192 additions and 2 deletions
@@ -23,6 +23,7 @@ from govoplan_core.core.modules import (
from govoplan_core.core.provider_governance import declared_module_architecture
from govoplan_core.core.templates import (
CAPABILITY_TEMPLATE_CATALOG,
CAPABILITY_TEMPLATE_CONTENT_LIBRARY,
CAPABILITY_TEMPLATE_RENDERER,
)
from govoplan_core.core.views import ViewSurface
@@ -95,6 +96,41 @@ DOCUMENTATION = (
],
},
),
DocumentationTopic(
id="templates.reusable-content",
title="Reusable content fragments and campaign parts",
summary="Create scoped, versioned content that authorized consumers can insert without copying a private library.",
body=(
"Content fragments retain text and HTML variants, usage constraints, locale, scope, revision, and publication state in Templates. "
"Campaign can load a fragment or complete email part through the optional content-library capability. Saving from Campaign creates "
"a draft and never publishes it automatically. Inserting content changes only the current Campaign draft; existing Campaign versions "
"and Template revisions remain unchanged."
),
layer="available",
documentation_types=("admin", "user"),
audience=("operator", "module_admin", "campaign_author"),
related_modules=("campaigns",),
translations={
"de": {
"title": "Wiederverwendbare Inhaltsbausteine und Kampagnenteile",
"summary": "Bereichsbezogene, versionierte Inhalte erstellen und in berechtigten Modulen verwenden.",
"body": (
"Inhaltsbausteine speichern Text- und HTML-Fassungen, Verwendungszwecke, Sprache, Geltungsbereich, Revision und "
"Veröffentlichungsstatus in Templates. Campaign kann Bausteine oder vollständige E-Mail-Teile über die optionale "
"Inhaltsbibliothek laden. Das Speichern aus Campaign legt einen Entwurf an und veröffentlicht ihn niemals automatisch. "
"Das Einfügen ändert nur den aktuellen Kampagnenentwurf; bestehende Kampagnenversionen und Template-Revisionen bleiben unverändert."
),
}
},
metadata={
"seed": True,
"help_contexts": [
"templates.field.type",
"templates.field.usages",
"campaign.template.content-library",
],
},
),
DocumentationTopic(
id="templates.printable-output",
title="Printable template output",
@@ -176,6 +212,12 @@ def _renderer(context: ModuleContext):
return renderer_capability(context)
def _content_library(context: ModuleContext):
from govoplan_templates.backend.capabilities import content_library_capability
return content_library_capability(context)
def _tenant_summary(session, tenant_id: str) -> dict[str, int]:
return {
"templates": session.query(template_models.TemplateDefinition).filter(
@@ -197,6 +239,10 @@ manifest = ModuleManifest(
optional_capabilities=(CAPABILITY_FILES_ARTIFACT_STORE,),
provides_interfaces=(
ModuleInterfaceProvider(name=CAPABILITY_TEMPLATE_CATALOG, version=MODULE_VERSION),
ModuleInterfaceProvider(
name=CAPABILITY_TEMPLATE_CONTENT_LIBRARY,
version=MODULE_VERSION,
),
ModuleInterfaceProvider(name=CAPABILITY_TEMPLATE_RENDERER, version=MODULE_VERSION),
),
requires_interfaces=(
@@ -248,6 +294,7 @@ manifest = ModuleManifest(
route_factory=_router,
capability_factories={
CAPABILITY_TEMPLATE_CATALOG: _catalog,
CAPABILITY_TEMPLATE_CONTENT_LIBRARY: _content_library,
CAPABILITY_TEMPLATE_RENDERER: _renderer,
},
tenant_summary_providers=(_tenant_summary,),