Verified with the coordinated workspace changes by devkit full run 2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed). This shared UI pass does not mark the individual module reviews complete.
1136 lines
57 KiB
Python
1136 lines
57 KiB
Python
from __future__ import annotations
|
|
|
|
from govoplan_core.core.modules import with_documentation_structured_translations
|
|
from govoplan_addresses.backend.german_structured_documentation import GERMAN_STRUCTURED_TRANSLATIONS
|
|
|
|
from dataclasses import replace
|
|
from pathlib import Path
|
|
|
|
from sqlalchemy import inspect
|
|
|
|
from govoplan_addresses.backend.capabilities import (
|
|
CAPABILITY_ADDRESSES_CONTACT_WRITER,
|
|
CAPABILITY_ADDRESSES_LOOKUP,
|
|
CAPABILITY_ADDRESSES_RECIPIENT_SOURCE,
|
|
)
|
|
from govoplan_addresses.backend.db import models as addresses_models # noqa: F401 - populate address ORM metadata
|
|
from govoplan_core.core.access import (
|
|
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
|
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
|
)
|
|
from govoplan_core.core.contact_points import (
|
|
CAPABILITY_ADDRESSES_CONTACT_POINT_RESOLUTION,
|
|
)
|
|
from govoplan_core.core.module_guards import (
|
|
drop_table_retirement_provider,
|
|
persistent_table_uninstall_guard,
|
|
)
|
|
from govoplan_core.core.people import CAPABILITY_ADDRESSES_PEOPLE_SEARCH
|
|
from govoplan_core.core.distribution_lists import CAPABILITY_RECIPIENT_CHANNEL_FACTS
|
|
from govoplan_core.core.modules import (
|
|
CapabilityDocumentation,
|
|
DocumentationCondition,
|
|
DocumentationTopic,
|
|
FrontendModule,
|
|
FrontendRoute,
|
|
MigrationSpec,
|
|
ModuleContext,
|
|
ModuleInterfaceProvider,
|
|
ModuleManifest,
|
|
NavItem,
|
|
PermissionDefinition,
|
|
ProductAreaContribution,
|
|
RoleTemplate,
|
|
)
|
|
from govoplan_core.core.provider_governance import (
|
|
ExternalProviderDeclaration,
|
|
ExternalProviderStateProviderRegistration,
|
|
ProviderBehaviorDeclaration,
|
|
ProviderObjectDeclaration,
|
|
declared_module_architecture,
|
|
)
|
|
from govoplan_core.core.views import ViewSurface
|
|
from govoplan_core.db.base import Base
|
|
from govoplan_addresses.backend.dsar_provider import ADDRESSES_DSAR_CAPABILITY
|
|
from govoplan_addresses.backend.provider_state import (
|
|
CARDDAV_PROVIDER_ID,
|
|
LDAP_PROVIDER_ID,
|
|
carddav_provider_states,
|
|
ldap_provider_states,
|
|
)
|
|
|
|
|
|
def _addresses_dsar_provider(context: ModuleContext) -> object:
|
|
del context
|
|
from govoplan_addresses.backend.dsar_provider import AddressesDsarProvider
|
|
|
|
return AddressesDsarProvider()
|
|
|
|
|
|
_addresses_table_retirement_provider = drop_table_retirement_provider(
|
|
addresses_models.AddressImportRun,
|
|
addresses_models.AddressImportProfile,
|
|
addresses_models.ContactFieldProvenance,
|
|
addresses_models.ContactRedirect,
|
|
addresses_models.ContactMergeRecord,
|
|
addresses_models.ContactPointQualityDecision,
|
|
addresses_models.ContactPointSnapshot,
|
|
addresses_models.AddressSyncDiagnostic,
|
|
addresses_models.AddressSyncConflict,
|
|
addresses_models.AddressSyncTombstone,
|
|
addresses_models.AddressSyncSource,
|
|
addresses_models.AddressListEntry,
|
|
addresses_models.AddressList,
|
|
addresses_models.ContactPostalAddress,
|
|
addresses_models.ContactPhone,
|
|
addresses_models.ContactEmail,
|
|
addresses_models.ContactChannelRule,
|
|
addresses_models.Contact,
|
|
addresses_models.AddressBook,
|
|
label="Addresses",
|
|
)
|
|
|
|
|
|
def _addresses_retirement_provider(session: object | None, module_id: str):
|
|
plan = _addresses_table_retirement_provider(session, module_id)
|
|
base_executor = plan.destroy_data_executor
|
|
if base_executor is None:
|
|
return plan
|
|
|
|
def executor(execute_session: object, execute_module_id: str) -> None:
|
|
if not hasattr(execute_session, "get_bind") or not hasattr(
|
|
execute_session, "query"
|
|
):
|
|
raise RuntimeError(
|
|
"No database session is available for Addresses credential retirement."
|
|
)
|
|
if inspect(execute_session.get_bind()).has_table(
|
|
addresses_models.AddressSyncSource.__tablename__
|
|
):
|
|
from govoplan_addresses.backend.service import (
|
|
audit_address_credentials_for_retirement,
|
|
)
|
|
|
|
audit_address_credentials_for_retirement(execute_session)
|
|
base_executor(execute_session, execute_module_id)
|
|
|
|
return replace(
|
|
plan,
|
|
destroy_data_warnings=(
|
|
*plan.destroy_data_warnings,
|
|
"Addresses-owned encrypted connector credentials are audited and deleted with the sync-source table.",
|
|
),
|
|
destroy_data_executor=executor,
|
|
)
|
|
|
|
|
|
def _permission(scope: str, label: str, description: str) -> PermissionDefinition:
|
|
module_id, resource, action = scope.split(":", 2)
|
|
return PermissionDefinition(
|
|
scope=scope,
|
|
label=label,
|
|
description=description,
|
|
category="Addresses",
|
|
level="tenant",
|
|
module_id=module_id,
|
|
resource=resource,
|
|
action=action,
|
|
)
|
|
|
|
|
|
PERMISSIONS = (
|
|
_permission(
|
|
"addresses:address_book:read",
|
|
"View address books",
|
|
"List address books visible to the current principal.",
|
|
),
|
|
_permission(
|
|
"addresses:address_book:write",
|
|
"Manage address books",
|
|
"Create and edit local address books.",
|
|
),
|
|
_permission(
|
|
"addresses:address_book:delete",
|
|
"Delete address books",
|
|
"Soft-delete local address books.",
|
|
),
|
|
_permission(
|
|
"addresses:address_book:admin",
|
|
"Administer address books",
|
|
"Manage system-scoped address books and future sync sources.",
|
|
),
|
|
_permission(
|
|
"addresses:address_list:read",
|
|
"View address lists",
|
|
"List reusable address lists and their entries.",
|
|
),
|
|
_permission(
|
|
"addresses:address_list:write",
|
|
"Manage address lists",
|
|
"Create and edit reusable address lists.",
|
|
),
|
|
_permission(
|
|
"addresses:address_list:delete",
|
|
"Delete address lists",
|
|
"Soft-delete reusable address lists.",
|
|
),
|
|
_permission(
|
|
"addresses:contact:read",
|
|
"View contacts",
|
|
"List and lookup contacts in visible address books.",
|
|
),
|
|
_permission(
|
|
"addresses:contact:write", "Manage contacts", "Create and edit local contacts."
|
|
),
|
|
_permission(
|
|
"addresses:contact:delete", "Delete contacts", "Soft-delete local contacts."
|
|
),
|
|
_permission(
|
|
"addresses:governance:read",
|
|
"View communication governance",
|
|
"Inspect effective-dated consent, suppression, and channel-preference facts.",
|
|
),
|
|
_permission(
|
|
"addresses:governance:write",
|
|
"Manage communication governance",
|
|
"Record and end consent, suppression, and channel-preference facts.",
|
|
),
|
|
_permission(
|
|
"addresses:sync:read",
|
|
"View address sync",
|
|
"Inspect address sync sources, conflicts, tombstones, and diagnostics.",
|
|
),
|
|
_permission(
|
|
"addresses:sync:write",
|
|
"Manage address sync",
|
|
"Bind address books to external sources and record sync state.",
|
|
),
|
|
_permission(
|
|
"addresses:sync:admin",
|
|
"Administer address sync",
|
|
"Administer address sync connectors and future destructive sync operations.",
|
|
),
|
|
)
|
|
|
|
|
|
ROLE_TEMPLATES = (
|
|
RoleTemplate(
|
|
slug="address_book_manager",
|
|
name="Address book manager",
|
|
description="Manage visible local address books and contacts.",
|
|
permissions=(
|
|
"addresses:address_book:read",
|
|
"addresses:address_book:write",
|
|
"addresses:address_book:delete",
|
|
"addresses:address_list:read",
|
|
"addresses:address_list:write",
|
|
"addresses:address_list:delete",
|
|
"addresses:contact:read",
|
|
"addresses:contact:write",
|
|
"addresses:contact:delete",
|
|
"addresses:governance:read",
|
|
"addresses:governance:write",
|
|
"addresses:sync:read",
|
|
"addresses:sync:write",
|
|
),
|
|
),
|
|
RoleTemplate(
|
|
slug="address_book_reader",
|
|
name="Address book reader",
|
|
description="Read visible address books and contacts.",
|
|
permissions=(
|
|
"addresses:address_book:read",
|
|
"addresses:address_list:read",
|
|
"addresses:contact:read",
|
|
"addresses:governance:read",
|
|
"addresses:sync:read",
|
|
),
|
|
),
|
|
)
|
|
|
|
|
|
def _tenant_summary(session, tenant_id: str) -> dict[str, int]:
|
|
from govoplan_addresses.backend.db.models import (
|
|
AddressBook,
|
|
AddressImportProfile,
|
|
AddressImportRun,
|
|
AddressList,
|
|
AddressSyncSource,
|
|
Contact,
|
|
ContactMergeRecord,
|
|
ContactPointQualityDecision,
|
|
ContactPointSnapshot,
|
|
)
|
|
|
|
return {
|
|
"address_books": session.query(AddressBook)
|
|
.filter(AddressBook.tenant_id == tenant_id, AddressBook.deleted_at.is_(None))
|
|
.count(),
|
|
"address_lists": session.query(AddressList)
|
|
.filter(AddressList.tenant_id == tenant_id, AddressList.deleted_at.is_(None))
|
|
.count(),
|
|
"contacts": session.query(Contact)
|
|
.filter(Contact.tenant_id == tenant_id, Contact.deleted_at.is_(None))
|
|
.count(),
|
|
"active_contact_merges": session.query(ContactMergeRecord)
|
|
.filter(
|
|
ContactMergeRecord.tenant_id == tenant_id,
|
|
ContactMergeRecord.status == "active",
|
|
)
|
|
.count(),
|
|
"contact_quality_decisions": session.query(ContactPointQualityDecision)
|
|
.filter(ContactPointQualityDecision.tenant_id == tenant_id)
|
|
.count(),
|
|
"contact_point_snapshots": session.query(ContactPointSnapshot)
|
|
.filter(ContactPointSnapshot.tenant_id == tenant_id)
|
|
.count(),
|
|
"sync_sources": session.query(AddressSyncSource)
|
|
.filter(
|
|
AddressSyncSource.tenant_id == tenant_id,
|
|
AddressSyncSource.enabled.is_(True),
|
|
)
|
|
.count(),
|
|
"address_import_profiles": session.query(AddressImportProfile)
|
|
.filter(
|
|
AddressImportProfile.tenant_id == tenant_id,
|
|
AddressImportProfile.is_current.is_(True),
|
|
)
|
|
.count(),
|
|
"address_import_runs": session.query(AddressImportRun)
|
|
.filter(AddressImportRun.tenant_id == tenant_id)
|
|
.count(),
|
|
}
|
|
|
|
|
|
def _addresses_router(_context: ModuleContext):
|
|
from govoplan_addresses.backend.router import router
|
|
|
|
return router
|
|
|
|
|
|
CARDDAV_PROVIDER = ExternalProviderDeclaration(
|
|
id=CARDDAV_PROVIDER_ID,
|
|
module_id="addresses",
|
|
label="CardDAV address-book synchronization",
|
|
maturity="synchronize",
|
|
operations=("discover", "read", "write", "delete", "synchronize", "preview"),
|
|
objects=(
|
|
ProviderObjectDeclaration(
|
|
object_type="address_book",
|
|
field_groups=("identity", "display", "sync_state"),
|
|
authority_modes=(
|
|
"external_authoritative",
|
|
"external_mirror",
|
|
"governed_sync",
|
|
),
|
|
default_authority_mode="external_mirror",
|
|
),
|
|
ProviderObjectDeclaration(
|
|
object_type="contact",
|
|
field_groups=(
|
|
"identity",
|
|
"name",
|
|
"postal",
|
|
"email",
|
|
"phone",
|
|
"source_metadata",
|
|
),
|
|
authority_modes=(
|
|
"external_authoritative",
|
|
"external_mirror",
|
|
"governed_sync",
|
|
),
|
|
default_authority_mode="governed_sync",
|
|
),
|
|
),
|
|
behavior=ProviderBehaviorDeclaration(
|
|
revision_tokens="CardDAV sync tokens, resource hrefs, and ETags are retained.",
|
|
concurrency="Conditional writes reject stale ETags and preserve explicit conflicts.",
|
|
freshness="Last attempt, last success, source status, and sync token are recorded.",
|
|
health="Transport failures, diagnostics, and unresolved conflicts are projected separately.",
|
|
max_read_items=5000,
|
|
idempotency="Stable source, href, UID, and ETag facts prevent duplicate contact effects.",
|
|
retry="Only a new governed sync attempt retries failed transport operations.",
|
|
timeout_seconds=30,
|
|
conflicts="Local and remote values remain in an explicit conflict record until resolved.",
|
|
outcome_unknown="Timed-out writes require a subsequent CardDAV read before correction or retry.",
|
|
outcome_unknown_supported=True,
|
|
evidence="Sync diagnostics, tombstones, conflicts, source revisions, and contact provenance are retained.",
|
|
audit_event_types=(
|
|
"addresses.sync.started",
|
|
"addresses.sync.finished",
|
|
"addresses.sync.conflict_recorded",
|
|
),
|
|
correction="A resolved conflict or later synchronized revision corrects state without rewriting prior evidence.",
|
|
rollback="Remote writes are not assumed to be transactionally reversible.",
|
|
compensation="A reconciled update or tombstone can compensate after the remote outcome is known.",
|
|
reconciliation="Read by resource href and compare ETag, UID, and local revision before applying changes.",
|
|
outage="Existing local contacts remain available with stale or unknown freshness.",
|
|
classifications=("personal", "confidential"),
|
|
purposes=("address-book synchronization", "governed recipient resolution"),
|
|
retention="Address-book and audit retention policies apply independently.",
|
|
secret_handling="Only credential references and sanitized authentication metadata are persisted in sync state.",
|
|
),
|
|
capability_names=(CAPABILITY_ADDRESSES_LOOKUP, CAPABILITY_ADDRESSES_CONTACT_WRITER),
|
|
documentation_topic_ids=("addresses.boundary",),
|
|
)
|
|
|
|
|
|
LDAP_PROVIDER = ExternalProviderDeclaration(
|
|
id=LDAP_PROVIDER_ID,
|
|
module_id="addresses",
|
|
label="Read-only LDAP and Active Directory contacts",
|
|
maturity="synchronize",
|
|
operations=("discover", "read", "preview", "synchronize"),
|
|
objects=(
|
|
ProviderObjectDeclaration(
|
|
object_type="contact",
|
|
field_groups=(
|
|
"identity",
|
|
"name",
|
|
"organization",
|
|
"postal",
|
|
"email",
|
|
"phone",
|
|
"source_metadata",
|
|
),
|
|
authority_modes=("external_authoritative", "external_mirror"),
|
|
default_authority_mode="external_authoritative",
|
|
),
|
|
),
|
|
behavior=ProviderBehaviorDeclaration(
|
|
revision_tokens="Stable LDAP source keys plus modifyTimestamp, uSNChanged, entryCSN, or a deterministic attribute digest are retained.",
|
|
concurrency="LDAP is authoritative and read-only; local projections are replaced only from a complete reviewed plan.",
|
|
freshness="Last attempt, last success, remote revision, and stale provider health remain visible.",
|
|
health="TLS, bind, discovery, paging, mapping, truncation, and malformed-entry failures are separate diagnostics.",
|
|
max_read_items=10000,
|
|
idempotency="The source binding, stable key, and revision prevent duplicate contact projections.",
|
|
retry="Failed reads are retried only by a new operator or scheduled sync attempt with bounded timeouts.",
|
|
timeout_seconds=120,
|
|
conflicts="Duplicate source keys, malformed mappings, and locally changed projections block or require a fresh plan.",
|
|
outcome_unknown="Read failures never infer external deletions and retain prior local projections as stale.",
|
|
outcome_unknown_supported=True,
|
|
evidence="Source keys, revisions, mapping configuration, diagnostics, tombstones, and normalized field provenance are retained.",
|
|
audit_event_types=(
|
|
"addresses.sync_source_created",
|
|
"addresses.sync_previewed",
|
|
"addresses.sync_completed",
|
|
),
|
|
correction="Correct the directory or mapping, then run a new full preview and synchronization.",
|
|
rollback="Prior projections remain reconstructable from source revision and contact change evidence; external LDAP is never mutated.",
|
|
compensation="A later authoritative refresh restores corrected projections.",
|
|
reconciliation="Only a complete paged search may infer an absent source object and create a local tombstone.",
|
|
outage="Existing contacts remain available and visibly stale; an unavailable directory never causes deletes.",
|
|
classifications=("personal", "confidential", "restricted"),
|
|
purposes=(
|
|
"directory projection",
|
|
"recipient resolution",
|
|
"identity-linked contact discovery",
|
|
),
|
|
retention="Address, audit, and records policies govern local projections and tombstone evidence.",
|
|
secret_handling="Bind secrets remain in reusable credential envelopes; URLs, previews, and diagnostics contain no credentials.",
|
|
),
|
|
capability_names=(CAPABILITY_ADDRESSES_LOOKUP, CAPABILITY_ADDRESSES_CONTACT_WRITER),
|
|
documentation_topic_ids=("addresses.ldap-directory",),
|
|
)
|
|
|
|
|
|
manifest = ModuleManifest(
|
|
id="addresses",
|
|
name="Addresses",
|
|
version="0.1.23",
|
|
required_capabilities=(
|
|
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
|
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
|
),
|
|
optional_dependencies=(
|
|
"campaigns",
|
|
"mail",
|
|
"forms",
|
|
"reporting",
|
|
"portal",
|
|
"postbox",
|
|
"connectors",
|
|
),
|
|
provides_interfaces=(
|
|
ModuleInterfaceProvider(name=CAPABILITY_ADDRESSES_LOOKUP, version="0.1.8"),
|
|
ModuleInterfaceProvider(
|
|
name=CAPABILITY_ADDRESSES_PEOPLE_SEARCH, version="0.1.0"
|
|
),
|
|
ModuleInterfaceProvider(
|
|
name=CAPABILITY_ADDRESSES_RECIPIENT_SOURCE, version="0.1.9"
|
|
),
|
|
ModuleInterfaceProvider(
|
|
name=CAPABILITY_ADDRESSES_CONTACT_POINT_RESOLUTION, version="1.0.0"
|
|
),
|
|
ModuleInterfaceProvider(
|
|
name=CAPABILITY_ADDRESSES_CONTACT_WRITER, version="0.1.8"
|
|
),
|
|
ModuleInterfaceProvider(
|
|
name=CAPABILITY_RECIPIENT_CHANNEL_FACTS, version="0.1.0"
|
|
),
|
|
ModuleInterfaceProvider(name=ADDRESSES_DSAR_CAPABILITY, version="0.1.0"),
|
|
),
|
|
permissions=PERMISSIONS,
|
|
route_factory=_addresses_router,
|
|
role_templates=ROLE_TEMPLATES,
|
|
tenant_summary_providers=(_tenant_summary,),
|
|
nav_items=(
|
|
NavItem(
|
|
path="/address-book",
|
|
label="Address Book",
|
|
icon="book-user",
|
|
required_any=("addresses:contact:read",),
|
|
order=80,
|
|
),
|
|
),
|
|
frontend=FrontendModule(
|
|
module_id="addresses",
|
|
package_name="@govoplan/addresses-webui",
|
|
routes=(
|
|
FrontendRoute(
|
|
path="/address-book",
|
|
component="AddressBookPage",
|
|
required_any=("addresses:contact:read",),
|
|
order=80,
|
|
),
|
|
),
|
|
nav_items=(
|
|
NavItem(
|
|
path="/address-book",
|
|
label="Address Book",
|
|
icon="book-user",
|
|
required_any=("addresses:contact:read",),
|
|
order=80,
|
|
),
|
|
),
|
|
product_areas=(
|
|
ProductAreaContribution(
|
|
id="people-responsibility",
|
|
module_id="addresses",
|
|
label="i18n:govoplan-core.product_area.people_responsibility",
|
|
icon="users",
|
|
description="i18n:govoplan-core.product_area.people_responsibility_description",
|
|
surface_ids=(
|
|
"addresses.nav.address.book",
|
|
"addresses.route.address.book",
|
|
),
|
|
order=70,
|
|
),
|
|
),
|
|
view_surfaces=(
|
|
ViewSurface(
|
|
id="addresses.page",
|
|
module_id="addresses",
|
|
kind="route",
|
|
label="Address Book",
|
|
order=80,
|
|
),
|
|
ViewSurface(
|
|
id="addresses.sources",
|
|
module_id="addresses",
|
|
kind="section",
|
|
label="Address sources",
|
|
order=10,
|
|
),
|
|
ViewSurface(
|
|
id="addresses.contacts",
|
|
module_id="addresses",
|
|
kind="section",
|
|
label="Contacts",
|
|
order=20,
|
|
),
|
|
ViewSurface(
|
|
id="addresses.detail",
|
|
module_id="addresses",
|
|
kind="section",
|
|
label="Contact detail",
|
|
order=30,
|
|
),
|
|
ViewSurface(
|
|
id="addresses.governance",
|
|
module_id="addresses",
|
|
kind="action",
|
|
label="Communication governance",
|
|
order=40,
|
|
),
|
|
ViewSurface(
|
|
id="addresses.sync",
|
|
module_id="addresses",
|
|
kind="action",
|
|
label="Address synchronization",
|
|
order=50,
|
|
),
|
|
),
|
|
),
|
|
migration_spec=MigrationSpec(
|
|
module_id="addresses",
|
|
metadata=Base.metadata,
|
|
script_location=str(Path(__file__).with_name("migrations") / "versions"),
|
|
retirement_supported=True,
|
|
retirement_provider=_addresses_retirement_provider,
|
|
retirement_notes="Destructive retirement drops address-owned database tables after the installer captures a database snapshot.",
|
|
),
|
|
capability_factories={
|
|
CAPABILITY_ADDRESSES_LOOKUP: lambda context: __import__(
|
|
"govoplan_addresses.backend.capabilities", fromlist=["lookup_capability"]
|
|
).lookup_capability(context),
|
|
CAPABILITY_ADDRESSES_PEOPLE_SEARCH: lambda context: __import__(
|
|
"govoplan_addresses.backend.capabilities",
|
|
fromlist=["people_search_capability"],
|
|
).people_search_capability(context),
|
|
CAPABILITY_ADDRESSES_RECIPIENT_SOURCE: lambda context: __import__(
|
|
"govoplan_addresses.backend.capabilities",
|
|
fromlist=["recipient_source_capability"],
|
|
).recipient_source_capability(context),
|
|
CAPABILITY_ADDRESSES_CONTACT_WRITER: lambda context: __import__(
|
|
"govoplan_addresses.backend.capabilities",
|
|
fromlist=["contact_writer_capability"],
|
|
).contact_writer_capability(context),
|
|
CAPABILITY_RECIPIENT_CHANNEL_FACTS: lambda context: __import__(
|
|
"govoplan_addresses.backend.capabilities",
|
|
fromlist=["channel_facts_capability"],
|
|
).channel_facts_capability(context),
|
|
CAPABILITY_ADDRESSES_CONTACT_POINT_RESOLUTION: lambda context: __import__(
|
|
"govoplan_addresses.backend.capabilities",
|
|
fromlist=["contact_point_resolution_capability"],
|
|
).contact_point_resolution_capability(context),
|
|
ADDRESSES_DSAR_CAPABILITY: _addresses_dsar_provider,
|
|
},
|
|
capability_documentation={
|
|
ADDRESSES_DSAR_CAPABILITY: CapabilityDocumentation(
|
|
label="Addresses data-subject request provider",
|
|
summary=(
|
|
"Finds bounded contact, contact-point, address-list, governance, "
|
|
"provenance, synchronization, and operator-attribution data without "
|
|
"exporting raw source payloads, connector state, or opaque evidence."
|
|
),
|
|
contract_version="0.1.0",
|
|
documentation_types=("admin",),
|
|
audience=("privacy_officer", "addresses_admin", "records_manager"),
|
|
),
|
|
},
|
|
uninstall_guard_providers=(
|
|
persistent_table_uninstall_guard(
|
|
addresses_models.AddressImportRun,
|
|
addresses_models.AddressImportProfile,
|
|
addresses_models.AddressSyncDiagnostic,
|
|
addresses_models.AddressSyncConflict,
|
|
addresses_models.AddressSyncTombstone,
|
|
addresses_models.AddressSyncSource,
|
|
addresses_models.AddressListEntry,
|
|
addresses_models.AddressList,
|
|
addresses_models.ContactFieldProvenance,
|
|
addresses_models.ContactRedirect,
|
|
addresses_models.ContactMergeRecord,
|
|
addresses_models.ContactPointQualityDecision,
|
|
addresses_models.ContactPointSnapshot,
|
|
addresses_models.AddressBook,
|
|
addresses_models.Contact,
|
|
addresses_models.ContactEmail,
|
|
addresses_models.ContactPhone,
|
|
addresses_models.ContactPostalAddress,
|
|
addresses_models.ContactChannelRule,
|
|
label="Addresses",
|
|
),
|
|
),
|
|
documentation=(
|
|
DocumentationTopic(
|
|
id="addresses.privacy.data-subject-requests",
|
|
title="Review Addresses data in a data-subject request",
|
|
summary=(
|
|
"Collect tenant-scoped contact data while preserving shared address, "
|
|
"recipient, synchronization, and provenance evidence."
|
|
),
|
|
body=(
|
|
"Addresses searches corroborated email and account selectors plus "
|
|
"namespaced contact and contact-point references. A matching contact "
|
|
"exports bounded identity, email, telephone, and postal values together "
|
|
"with its address-list use and minimized governance, quality, provenance, "
|
|
"merge, redirect, and synchronization evidence. Account matches add only "
|
|
"minimized operator attribution for governed configuration and evidence. "
|
|
"The provider excludes raw imported or synchronized source payloads, "
|
|
"connector tokens and revisions that could act as credentials, opaque "
|
|
"metadata, snapshot request and resolution payloads, import plans, merge "
|
|
"before/after payloads, unrelated contacts, and other tenants. Quality, "
|
|
"governance, provenance, merge, redirect, synchronization, import, "
|
|
"snapshot, and operator evidence is retained with an explicit reason. "
|
|
"Because reusable contacts can be shared, synchronized, merged, or "
|
|
"referenced by immutable recipient snapshots, the DSAR provider never "
|
|
"deletes them automatically. An authorized operator must review "
|
|
"dependencies and use the normal Addresses correction, archive, source, "
|
|
"merge, or governance workflow."
|
|
),
|
|
layer="static",
|
|
documentation_types=("admin",),
|
|
audience=(
|
|
"privacy_officer",
|
|
"addresses_admin",
|
|
"records_manager",
|
|
"operator",
|
|
),
|
|
related_modules=(
|
|
"access",
|
|
"audit",
|
|
"campaigns",
|
|
"dist_lists",
|
|
"records",
|
|
),
|
|
order=29,
|
|
translations={
|
|
"de": {
|
|
"title": "Addresses-Daten in einer Betroffenenanfrage prüfen",
|
|
"summary": (
|
|
"Mandantenbezogene Kontaktdaten erfassen und dabei gemeinsame Adress-, Empfänger-, Synchronisations- und "
|
|
"Herkunftsnachweise bewahren."
|
|
),
|
|
"body": (
|
|
"Addresses durchsucht bestätigte E-Mail- und Kontoselektoren sowie namensraumgebundene Kontakt- und "
|
|
"Kontaktpunktverweise. Zu einem passenden Kontakt werden begrenzte Identitäts-, E-Mail-, Telefon- und Postwerte "
|
|
"einschließlich seiner Adresslistennutzung und minimierter Nachweise zu Governance, Qualität, Herkunft, Zusammenführung, "
|
|
"Weiterleitung und Synchronisation exportiert. Kontotreffer ergänzen nur minimierte Zuordnungen von Betriebspersonen zu "
|
|
"gesteuerter Konfiguration und Nachweisen. Ausgeschlossen sind rohe importierte oder synchronisierte Quelldaten, "
|
|
"Connector-Token und Revisionen mit Zugangsdatencharakter, undurchsichtige Metadaten, Anfrage- und Auflösungsnutzdaten "
|
|
"von Snapshots, Importpläne, Vorher-/Nachher-Daten von Zusammenführungen, unbeteiligte Kontakte und andere Mandanten. "
|
|
"Nachweise zu Qualität, Governance, Herkunft, Zusammenführung, Weiterleitung, Synchronisation, Import, Snapshot und "
|
|
"Betriebszuordnung werden mit ausdrücklicher Begründung aufbewahrt. Weil wiederverwendbare Kontakte geteilt, "
|
|
"synchronisiert, zusammengeführt oder von unveränderlichen Empfänger-Snapshots referenziert sein können, löscht der "
|
|
"DSAR-Provider sie niemals automatisch. Eine berechtigte Betriebsperson muss Abhängigkeiten prüfen und den regulären "
|
|
"Addresses-Ablauf für Korrektur, Archivierung, Quelle, Zusammenführung oder Governance verwenden."
|
|
),
|
|
}
|
|
},
|
|
metadata={
|
|
"seed": True,
|
|
"help_contexts": [
|
|
"addresses.contacts",
|
|
"addresses.governance",
|
|
"addresses.action.archive",
|
|
],
|
|
},
|
|
),
|
|
DocumentationTopic(
|
|
id="addresses.boundary",
|
|
title="Reusable address ownership",
|
|
summary="Reusable person, organization, household, postal, and email recipient sources belong to the addresses module.",
|
|
body=(
|
|
"Open the book beside Address books for documentation of the address workspace. "
|
|
"Campaigns may keep immutable campaign-local recipient snapshots, but durable address directories, "
|
|
"recipient-source definitions, consent metadata, provenance, deduplication, and import/export workflows "
|
|
"are owned by govoplan-addresses. The Address Book workspace keeps Reload directly "
|
|
"before Add address book at the upper right. Import / export, Connections, and Address "
|
|
"quality open labelled, scoped tools; Manage applies to the selected book or list and "
|
|
"separates archive actions from editing. Export version is chosen in Import / export. "
|
|
"Contact creation remains beside the contact list. Folder icons alone expand or collapse "
|
|
"the tree; labels select a group, book, or list without changing expansion. A selected "
|
|
"group is navigation, not an aggregate contact book. Reload preserves collapsed "
|
|
"branches. All permission and read-only reasons, confirmations, import safeguards, "
|
|
"and contact-to-list drag and drop still apply."
|
|
),
|
|
layer="configured",
|
|
documentation_types=("admin", "user"),
|
|
audience=("tenant_admin", "operator", "module_admin"),
|
|
related_modules=(
|
|
"campaigns",
|
|
"mail",
|
|
"forms",
|
|
"reporting",
|
|
"portal",
|
|
"postbox",
|
|
),
|
|
order=30,
|
|
translations={
|
|
"de": {
|
|
"title": "Zuständigkeit für wiederverwendbare Adressen",
|
|
"summary": (
|
|
"Wiederverwendbare Personen-, Organisations-, Haushalts-, Post- und E-Mail-Empfängerquellen gehören dem "
|
|
"Addresses-Modul."
|
|
),
|
|
"body": (
|
|
"Öffnen Sie das Buch neben Adressbücher für die Dokumentation des Adressarbeitsbereichs. "
|
|
"Campaigns darf unveränderliche campaignlokale Empfänger-Snapshots halten. Dauerhafte Adressverzeichnisse, "
|
|
"Empfängerquellendefinitionen, Einwilligungsmetadaten, Herkunft, Dublettenbereinigung sowie Import- und Exportabläufe "
|
|
"gehören jedoch govoplan-addresses. Im Adressbuch steht Neuladen oben rechts unmittelbar vor "
|
|
"Adressbuch hinzufügen. Import / Export, Verbindungen und Adressqualität öffnen beschriftete, "
|
|
"kontextbezogene Werkzeuge. Verwalten bezieht sich auf das ausgewählte Adressbuch oder die Liste "
|
|
"und trennt Archivieren vom Bearbeiten. Die Exportversion wird unter Import / Export gewählt. "
|
|
"Kontakte werden weiterhin direkt neben der Kontaktliste angelegt. Nur Ordnersymbole klappen "
|
|
"den Baum auf oder zu; Beschriftungen wählen eine Gruppe, ein Adressbuch oder eine Liste aus, "
|
|
"ohne die Aufklappstellung zu ändern. Eine ausgewählte Gruppe dient der Navigation und ist "
|
|
"kein zusammengefasstes Adressbuch. Neuladen bewahrt zugeklappte Zweige. Berechtigungs- und "
|
|
"Schreibschutzgründe, Bestätigungen, Importsicherungen und das Ziehen von Kontakten in Listen gelten unverändert."
|
|
),
|
|
}
|
|
},
|
|
metadata={
|
|
"seed": True,
|
|
"help_contexts": [
|
|
"addresses.page",
|
|
"addresses.explorer.transfer",
|
|
"addresses.sources",
|
|
"addresses.contacts",
|
|
"addresses.detail",
|
|
"addresses.state.read-only",
|
|
],
|
|
},
|
|
),
|
|
DocumentationTopic(
|
|
id="addresses.contact-point-resolution",
|
|
title="Contact-point resolution and snapshots",
|
|
summary="Resolve purpose-aware channel targets and freeze immutable recipient evidence.",
|
|
body=(
|
|
"Addresses exposes a versioned contact-point capability for email, postal, internal-mail, and portal targets. "
|
|
"Callers can request an effective date, communication purpose, address purpose, fallback rule, locale, and "
|
|
"postal format. Bounded previews remain live; frozen snapshots retain the resolved values, exclusions, "
|
|
"source and governance revisions, provenance, and a deterministic evidence hash even after contacts change."
|
|
),
|
|
layer="configured",
|
|
documentation_types=("admin", "user"),
|
|
audience=("tenant_admin", "operator", "module_admin"),
|
|
related_modules=("dist_lists", "campaigns", "policy", "templates"),
|
|
order=31,
|
|
translations={
|
|
"de": {
|
|
"title": "Kontaktpunktauflösung und Snapshots",
|
|
"summary": "Zweckbezogene Kanalziele auflösen und unveränderliche Empfängernachweise einfrieren.",
|
|
"body": (
|
|
"Addresses stellt eine versionierte Kontaktpunktfähigkeit für E-Mail-, Post-, Hauspost- und Portalziele bereit. "
|
|
"Aufrufende können Wirksamkeitsdatum, Kommunikationszweck, Adresszweck, Rückfallregel, Spracheinstellung und Postformat "
|
|
"angeben. Begrenzte Vorschauen bleiben aktuell; eingefrorene Snapshots bewahren aufgelöste Werte, Ausschlüsse, Quellen- "
|
|
"und Governance-Revisionen, Herkunft und einen deterministischen Nachweishash auch nach späteren Kontaktänderungen."
|
|
),
|
|
}
|
|
},
|
|
metadata={
|
|
"seed": True,
|
|
"help_contexts": [
|
|
"addresses.governance",
|
|
"addresses.field.channel",
|
|
"addresses.field.contact-point",
|
|
"addresses.field.communication-purpose",
|
|
"addresses.field.effective-period",
|
|
],
|
|
},
|
|
),
|
|
DocumentationTopic(
|
|
id="addresses.tabular-imports",
|
|
title="CSV, XLSX, and LDIF contact imports",
|
|
summary="Preview and apply reusable, versioned contact mappings without silent row or entry loss.",
|
|
body=(
|
|
"CSV, XLSX, and LDIF files can be mapped with scoped, reusable profile versions. Each preview validates headers or attributes, "
|
|
"encodings, source keys, duplicates, blank values, format limits, and contact identity before any mutation. "
|
|
"The reviewed input hash and plan hash are retained with row-level effects and diagnostics. Apply is idempotent, "
|
|
"rejects contacts changed after preview, and records sufficient evidence for a guarded rollback. Each new update "
|
|
"uses version-2 before-images for editable contact values, source metadata, complete contact-point identities, "
|
|
"original and normalized values, provenance, order and timestamps, and prior deletion state: rolling back an "
|
|
"import that restored a deleted contact archives it again. Older update runs without complete before-images require "
|
|
"manual reconciliation; automatic rollback stops before changing any contacts. Created-contact identities and "
|
|
"after-hashes are persisted with the applied plan and required for rollback; missing older guards or contacts "
|
|
"moved to another book also require reconciliation. Point-evidence edits are included in change guards. "
|
|
"Unchanged point values retain their identities and original evidence. Removing or replacing a point referenced "
|
|
"by an address list or point-specific consent/quality decision requires explicit reconciliation, including when "
|
|
"a new reference would otherwise be detached by rollback. Current book visibility and change "
|
|
"guards still apply. Preview source lookups and contact collections are loaded in bounded batches, without changing "
|
|
"duplicate policies, reviewed hashes, or apply-time validation. A persisted run "
|
|
"can be reopened with its run link after navigation or reload; previewed, applied, rolled-back, expired, and "
|
|
"unavailable states remain explicit. Both apply and rollback submit the reviewed plan hash. Missing, expired, "
|
|
"hidden, and cross-tenant runs disclose no source payload. XLSX formulas, macros, and legacy workbook formats "
|
|
"are never executed or imported. LDIF folded lines, UTF-8 and base64 text, repeated attributes, and comments are parsed; "
|
|
"binary and URL values are never projected or fetched. Change records default to rejected diagnostics and may only be ignored "
|
|
"or treat add records as static entries through an explicit profile policy."
|
|
),
|
|
layer="configured",
|
|
documentation_types=("admin", "user"),
|
|
audience=("tenant_admin", "module_admin", "power_user"),
|
|
conditions=(
|
|
DocumentationCondition(
|
|
required_modules=("addresses",),
|
|
any_scopes=("addresses:contact:write", "addresses:sync:write"),
|
|
),
|
|
),
|
|
related_modules=("connectors", "datasources", "dataflow", "files", "audit"),
|
|
order=33,
|
|
translations={
|
|
"de": {
|
|
"title": "Kontakte aus CSV, XLSX und LDIF importieren",
|
|
"summary": (
|
|
"Wiederverwendbare, versionierte Kontaktzuordnungen vorprüfen und anwenden, ohne Zeilen oder Einträge "
|
|
"stillschweigend zu verlieren."
|
|
),
|
|
"body": (
|
|
"CSV-, XLSX- und LDIF-Dateien lassen sich mit bereichsgebundenen, wiederverwendbaren Profilversionen zuordnen. Jede "
|
|
"Vorschau prüft Überschriften oder Attribute, Kodierung, Quellschlüssel, Dubletten, Leerwerte, Formatgrenzen und "
|
|
"Kontaktidentität vor jeder Änderung. Der geprüfte Eingabe- und Planhash wird mit zeilenbezogenen Wirkungen und Diagnosen "
|
|
"aufbewahrt. Die Anwendung ist idempotent, verwirft seit der Vorschau geänderte Kontakte und zeichnet ausreichende "
|
|
"Nachweise für eine gesicherte Rücknahme auf. Ein gespeicherter Lauf kann nach Navigation oder Neuladen über seinen Link "
|
|
"erneut geöffnet werden. Vorher-Bilder der Version 2 enthalten bearbeitbare Kontaktwerte, Quellmetadaten, "
|
|
"vollständige Kontaktpunktkennungen, Original- und normalisierte Werte, Herkunft, Reihenfolge, Zeitstempel und den "
|
|
"vorherigen Löschzustand: Die Rücknahme archiviert einen durch den Import wiederhergestellten Kontakt erneut. "
|
|
"Ältere Änderungsläufe ohne vollständige Vorher-Bilder erfordern einen manuellen Abgleich; die automatische "
|
|
"Rücknahme stoppt vor jeder Kontaktänderung. Kennungen neu angelegter Kontakte und Nachher-Hashes werden mit dem "
|
|
"angewendeten Plan gespeichert und sind für die Rücknahme erforderlich; fehlende ältere Sicherungen oder in ein "
|
|
"anderes Buch verschobene Kontakte erfordern ebenfalls einen Abgleich. Änderungen an Punktnachweisen werden vom "
|
|
"Änderungsschutz erfasst. Unveränderte Punktwerte behalten Kennung und Originalnachweise. Das Entfernen oder "
|
|
"Ersetzen eines in Adresslisten oder punktspezifischen Einwilligungs-/Qualitätsentscheidungen referenzierten "
|
|
"Punkts erfordert einen ausdrücklichen Abgleich; dies gilt auch für neue Referenzen, die eine Rücknahme sonst "
|
|
"lösen würde. Aktuelle Adressbuchsichtbarkeit und "
|
|
"Änderungsschutz bleiben wirksam. "
|
|
"Quellzuordnungen und Kontaktpunkte werden für die Vorschau in begrenzten Stapeln geladen, ohne Dublettenregeln, "
|
|
"geprüfte Hashes oder die erneute Prüfung bei Anwendung zu ändern. Ein gespeicherter Lauf kann über seinen Link "
|
|
"erneut geöffnet werden; Vorschau-, Anwendungs-, Rücknahme-, Ablauf- und Nichtverfügbarkeitszustände bleiben eindeutig. "
|
|
"Anwendung und Rücknahme übermitteln den geprüften Planhash. Fehlende, abgelaufene, verborgene und mandantenfremde Läufe "
|
|
"legen keine Quelldaten offen. XLSX-Formeln, Makros und ältere Arbeitsmappenformate werden niemals ausgeführt oder "
|
|
"importiert. Gefaltete LDIF-Zeilen, UTF-8- und Base64-Text, wiederholte Attribute und Kommentare werden verarbeitet; "
|
|
"Binär- und URL-Werte werden weder projiziert noch abgerufen. Änderungsdatensätze gelten standardmäßig als abgelehnte "
|
|
"Diagnose und dürfen nur über eine ausdrückliche Profilrichtlinie ignoriert oder bei Add-Einträgen als statische Daten "
|
|
"behandelt werden."
|
|
),
|
|
}
|
|
},
|
|
metadata={
|
|
"kind": "workflow",
|
|
"help_contexts": [
|
|
"addresses.action.import",
|
|
"addresses.contacts",
|
|
"addresses.sources",
|
|
],
|
|
},
|
|
),
|
|
DocumentationTopic(
|
|
id="addresses.vcard-batches",
|
|
title="Selective vCard batch import and export",
|
|
summary="Preview multiple vCard files, choose each card's effect, and export deterministic scoped files.",
|
|
body=(
|
|
"One or more UTF-8 .vcf files are parsed into a persisted, non-mutating preview with bounded diagnostics, "
|
|
"duplicate suggestions, an input hash, a parser version, and a deterministic plan hash. Operators choose "
|
|
"create, update, or ignore only where the reviewed plan permits it. Apply rejects stale contact targets and "
|
|
"is idempotent for the same selection; a different retry is rejected. Pending runs can be reloaded or cancelled "
|
|
"without changing contacts. Upload size, file count, card count, line count, and unfolded-line length are bounded. "
|
|
"Exports can target a complete address book, one address list, or explicit contacts; vCard 3.0 or 4.0 is selected "
|
|
"explicitly and contacts use deterministic display-name and stable-ID ordering. Export and import evidence records "
|
|
"hashes and counts, while diagnostics never disclose raw contact payloads. Large previews remain persisted and expose "
|
|
"their batch execution mode so a runtime job capability can execute them asynchronously when available."
|
|
),
|
|
layer="configured",
|
|
documentation_types=("admin", "user"),
|
|
audience=("tenant_admin", "operator", "module_admin", "power_user"),
|
|
related_modules=("files", "audit", "connectors"),
|
|
order=34,
|
|
translations={
|
|
"de": {
|
|
"title": "vCard-Stapel selektiv importieren und exportieren",
|
|
"summary": (
|
|
"Mehrere vCard-Dateien vorprüfen, die Wirkung jeder Karte wählen und deterministische bereichsgebundene Dateien exportieren."
|
|
),
|
|
"body": (
|
|
"Eine oder mehrere UTF-8-.vcf-Dateien werden in eine gespeicherte, nicht verändernde Vorschau mit begrenzten Diagnosen, "
|
|
"Dublettenhinweisen, Eingabehash, Parser-Version und deterministischem Planhash eingelesen. Betriebspersonen wählen "
|
|
"Anlegen, Aktualisieren oder Ignorieren nur dort, wo der geprüfte Plan es erlaubt. Die Anwendung verwirft veraltete "
|
|
"Kontaktziele und ist für dieselbe Auswahl idempotent; eine abweichende Wiederholung wird abgelehnt. Ausstehende Läufe "
|
|
"lassen sich neu laden oder abbrechen, ohne Kontakte zu verändern. Uploadgröße, Datei- und Kartenanzahl, Zeilenanzahl und "
|
|
"Länge entfalteter Zeilen sind begrenzt. Exporte können ein vollständiges Adressbuch, eine Adressliste oder ausgewählte "
|
|
"Kontakte umfassen; vCard 3.0 oder 4.0 wird ausdrücklich gewählt und Kontakte werden deterministisch nach Anzeigename und "
|
|
"stabiler Kennung sortiert. Export- und Importnachweise speichern Hashes und Anzahlen, während Diagnosen niemals rohe "
|
|
"Kontaktdaten offenlegen. Große Vorschauen bleiben gespeichert und geben ihren Stapelausführungsmodus an, sodass eine "
|
|
"Laufzeit-Jobfähigkeit sie bei Verfügbarkeit asynchron ausführen kann."
|
|
),
|
|
}
|
|
},
|
|
metadata={
|
|
"seed": True,
|
|
"help_contexts": [
|
|
"addresses.action.import",
|
|
"addresses.contacts",
|
|
"addresses.sources",
|
|
],
|
|
},
|
|
),
|
|
DocumentationTopic(
|
|
id="addresses.ldap-directory",
|
|
title="LDAP and Active Directory address sources",
|
|
summary="Project authoritative directory contacts through a bounded, read-only synchronization source.",
|
|
body=(
|
|
"LDAP sources use LDAPS or StartTLS and reusable credential envelopes. Discovery finds available base DNs; "
|
|
"the source profile then controls a bounded paged filter and explicit attribute mapping. Preview never mutates "
|
|
"contacts. A complete successful read may create, update, or tombstone local projections; truncated or failed "
|
|
"reads suppress absence-based deletes and mark the source stale. Stable source keys, revisions, normalized fields, "
|
|
"and provenance remain attached to every retained contact."
|
|
),
|
|
layer="configured",
|
|
documentation_types=("admin", "user"),
|
|
audience=("tenant_admin", "operator", "module_admin"),
|
|
related_modules=("connectors", "idm", "access", "policy", "audit"),
|
|
order=35,
|
|
translations={
|
|
"de": {
|
|
"title": "LDAP- und Active-Directory-Adressquellen",
|
|
"summary": (
|
|
"Maßgebliche Verzeichniskontakte über eine begrenzte, schreibgeschützte Synchronisationsquelle projizieren."
|
|
),
|
|
"body": (
|
|
"LDAP-Quellen verwenden LDAPS oder StartTLS und wiederverwendbare Zugangsdatenhüllen. Die Ermittlung findet verfügbare "
|
|
"Basis-DNs; anschließend steuert das Quellprofil einen begrenzten seitenweisen Filter und eine ausdrückliche "
|
|
"Attributzuordnung. Die Vorschau verändert niemals Kontakte. Ein vollständiger erfolgreicher Lesevorgang darf lokale "
|
|
"Projektionen anlegen, aktualisieren oder als entfernt markieren; abgeschnittene oder fehlgeschlagene Lesevorgänge "
|
|
"unterdrücken Löschungen aufgrund von Abwesenheit und markieren die Quelle als veraltet. Stabile Quellschlüssel, "
|
|
"Revisionen, normalisierte Felder und Herkunft bleiben mit jedem erhaltenen Kontakt verbunden."
|
|
),
|
|
}
|
|
},
|
|
),
|
|
DocumentationTopic(
|
|
id="addresses.quality-and-merge",
|
|
title="Contact quality, duplicates, and reversible merges",
|
|
summary="Review address quality and duplicate suggestions without losing source evidence.",
|
|
body=(
|
|
"Addresses preserves original and normalized contact-point values, records field-level provenance, "
|
|
"and projects invalid, returned, stale, or undeliverable states into recipient resolution with stable "
|
|
"reason codes. Duplicate suggestions are bounded and explain their matching features. An operator can "
|
|
"choose the surviving values, merge contact points, and later undo or split the merge while the recorded "
|
|
"post-merge evidence still matches. Contact redirects keep stored references resolvable, and address-list "
|
|
"memberships are repaired transactionally. Audit remains an optional integration; the Addresses change "
|
|
"sequence and merge evidence are always retained."
|
|
),
|
|
layer="configured",
|
|
documentation_types=("admin", "user"),
|
|
audience=("tenant_admin", "operator", "module_admin"),
|
|
related_modules=("campaigns", "dist_lists", "policy", "audit"),
|
|
order=32,
|
|
translations={
|
|
"de": {
|
|
"title": "Kontaktqualität, Dubletten und umkehrbare Zusammenführungen",
|
|
"summary": "Adressqualität und Dublettenvorschläge prüfen, ohne Quellnachweise zu verlieren.",
|
|
"body": (
|
|
"Addresses bewahrt ursprüngliche und normalisierte Kontaktpunktwerte, zeichnet die Herkunft je Feld auf und überführt "
|
|
"ungültige, zurückgesandte, veraltete oder unzustellbare Zustände mit stabilen Grundcodes in die Empfängerauflösung. "
|
|
"Dublettenvorschläge sind begrenzt und erklären ihre Übereinstimmungsmerkmale. Eine Betriebsperson kann die zu erhaltenden "
|
|
"Werte wählen, Kontaktpunkte zusammenführen und die Zusammenführung später rückgängig machen oder aufteilen, solange der "
|
|
"aufgezeichnete Nachweis nach der Zusammenführung noch übereinstimmt. Kontaktweiterleitungen halten gespeicherte Verweise "
|
|
"auflösbar, und Mitgliedschaften in Adresslisten werden transaktional repariert. Audit bleibt eine optionale Integration; "
|
|
"die Änderungsfolge und Zusammenführungsnachweise von Addresses werden stets aufbewahrt."
|
|
),
|
|
}
|
|
},
|
|
),
|
|
DocumentationTopic(
|
|
id="addresses.reference.fields-and-consequences",
|
|
title="Address fields, scope, and action consequences",
|
|
summary="Scope, source authority, contact points, list membership, archival, synchronization, and merge consequences.",
|
|
body=(
|
|
"Address books are scoped to a user, group, tenant, or authorized system context. Inherited and externally authoritative "
|
|
"books may remain visible but read-only. Contacts own reusable name, organization, electronic, phone, postal, tag, note, "
|
|
"quality, and provenance facts; address lists reference contact points from the same book and do not replace Distribution "
|
|
"Lists. Archival hides a book, list, or contact from ordinary selection while preserving governed history and references. "
|
|
"CardDAV and LDAP sources expose their direction, authority, freshness, diagnostics, conflict, and stale-state behavior. "
|
|
"Imports and synchronization require preview before mutation. Contact merges select a survivor and field provenance, repair "
|
|
"list references transactionally, and retain redirects and evidence so a matching merge can be undone or split."
|
|
),
|
|
layer="configured",
|
|
documentation_types=("admin", "user"),
|
|
audience=("tenant_admin", "operator", "module_admin", "power_user"),
|
|
related_modules=(
|
|
"dist_lists",
|
|
"connectors",
|
|
"datasources",
|
|
"campaigns",
|
|
"policy",
|
|
"audit",
|
|
),
|
|
order=36,
|
|
translations={
|
|
"de": {
|
|
"title": "Adressfelder, Geltungsbereiche und Folgen von Aktionen",
|
|
"summary": (
|
|
"Geltungsbereich, Quellenhoheit, Kontaktpunkte, Listenmitgliedschaft sowie Folgen von Archivierung, Synchronisation und "
|
|
"Zusammenführung."
|
|
),
|
|
"body": (
|
|
"Adressbücher sind einer Person, Gruppe, einem Mandanten oder einem berechtigten Systemkontext zugeordnet. Geerbte und "
|
|
"extern maßgebliche Bücher können sichtbar, aber schreibgeschützt bleiben. Kontakte besitzen wiederverwendbare Angaben "
|
|
"zu Name, Organisation, elektronischen und telefonischen Kontaktpunkten, Postanschrift, Schlagwörtern, Notizen, Qualität "
|
|
"und Herkunft. Adresslisten verweisen auf Kontaktpunkte desselben Buchs und ersetzen keine Distribution Lists. Eine "
|
|
"Archivierung entfernt Buch, Liste oder Kontakt aus der gewöhnlichen Auswahl, bewahrt aber gesteuerte Historie und "
|
|
"Verweise. CardDAV- und LDAP-Quellen zeigen Richtung, Hoheit, Aktualität, Diagnosen, Konflikte und Verhalten bei "
|
|
"veraltetem Zustand. Import und Synchronisation erfordern vor jeder Änderung eine Vorschau. Kontaktzusammenführungen "
|
|
"wählen überlebenden Kontakt und Feldherkunft, reparieren Listenverweise transaktional und bewahren Weiterleitungen und "
|
|
"Nachweise, sodass eine passende Zusammenführung rückgängig gemacht oder aufgeteilt werden kann."
|
|
),
|
|
}
|
|
},
|
|
metadata={
|
|
"kind": "reference",
|
|
"seed": True,
|
|
"help_contexts": [
|
|
"addresses.field.book-scope",
|
|
"addresses.field.contact-identity",
|
|
"addresses.field.organization",
|
|
"addresses.field.contact-point",
|
|
"addresses.action.archive",
|
|
"addresses.action.import",
|
|
"addresses.action.sync",
|
|
"addresses.action.merge",
|
|
],
|
|
"consequence_classes": {
|
|
"archive": "Removes the object from ordinary selection while retaining governed history and references.",
|
|
"import_or_sync": "Applies only a reviewed bounded plan and retains source revision, diagnostics, and provenance.",
|
|
"merge": "Repoints governed references to a survivor and retains reversible redirect and provenance evidence.",
|
|
"governance_fact": "Adds or ends an effective-dated communication decision without erasing prior facts.",
|
|
},
|
|
},
|
|
),
|
|
),
|
|
external_providers=(CARDDAV_PROVIDER, LDAP_PROVIDER),
|
|
external_provider_state_providers=(
|
|
ExternalProviderStateProviderRegistration(
|
|
module_id="addresses",
|
|
provider_id=CARDDAV_PROVIDER_ID,
|
|
provider=carddav_provider_states,
|
|
),
|
|
ExternalProviderStateProviderRegistration(
|
|
module_id="addresses",
|
|
provider_id=LDAP_PROVIDER_ID,
|
|
provider=ldap_provider_states,
|
|
),
|
|
),
|
|
architecture=declared_module_architecture(
|
|
layer="communication_participation",
|
|
kind="domain",
|
|
maturity="vertical_slice",
|
|
documentation_ref="docs/ADDRESS_MODULE_ARCHITECTURE.md",
|
|
test_ref="tests/test_addresses_service.py",
|
|
known_limits=(
|
|
"External address-book synchronization remains a bounded connector slice rather than a supported provider profile.",
|
|
),
|
|
supported_authority_modes=(
|
|
"native_authoritative",
|
|
"external_authoritative",
|
|
"external_mirror",
|
|
"governed_sync",
|
|
),
|
|
owned_concepts=(
|
|
"contact point",
|
|
"address book",
|
|
"contact consent",
|
|
"recipient source",
|
|
),
|
|
non_owned_concepts=(
|
|
"identity",
|
|
"organization",
|
|
"campaign recipient snapshot",
|
|
"procedure party",
|
|
),
|
|
target_tested_providers=(CARDDAV_PROVIDER_ID,),
|
|
security_docs=("docs/ADDRESS_MODULE_ARCHITECTURE.md",),
|
|
operations_docs=("README.md",),
|
|
),
|
|
)
|
|
|
|
|
|
manifest = with_documentation_structured_translations(
|
|
manifest, locale="de", translations=GERMAN_STRUCTURED_TRANSLATIONS
|
|
)
|
|
|
|
|
|
def get_manifest() -> ModuleManifest:
|
|
return manifest
|