feat: manage custom appearance policy
This commit is contained in:
@@ -15,7 +15,12 @@ from govoplan_core.audit.logging import audit_from_principal, audit_operation_co
|
||||
from govoplan_admin.backend.db.models import GovernanceTemplate, GovernanceTemplateAssignment
|
||||
from govoplan_core.admin.common import AdminConflictError, AdminValidationError
|
||||
from govoplan_core.core.access import CAPABILITY_ACCESS_ADMINISTRATION, AccessAdministration
|
||||
from govoplan_core.core.appearance import appearance_settings, update_appearance_settings
|
||||
from govoplan_core.core.appearance import (
|
||||
appearance_custom_overrides_policy,
|
||||
appearance_settings,
|
||||
update_appearance_custom_overrides_policy,
|
||||
update_appearance_settings,
|
||||
)
|
||||
from govoplan_core.core.change_sequence import (
|
||||
decode_sequence_watermark,
|
||||
encode_sequence_watermark,
|
||||
@@ -311,6 +316,7 @@ def _system_settings_item(session: Session) -> SystemSettingsItem:
|
||||
navigation=navigation.as_dict() if navigation is not None else None,
|
||||
appearance_palette=appearance_palette or "default",
|
||||
appearance_palette_locked=appearance_palette_locked,
|
||||
appearance_custom_overrides_allowed=appearance_custom_overrides_policy(item.settings) is True,
|
||||
)
|
||||
|
||||
|
||||
@@ -333,6 +339,7 @@ def _system_settings_sections(item: SystemSettingsItem) -> dict[str, Any]:
|
||||
"appearance": {
|
||||
"appearance_palette": payload["appearance_palette"],
|
||||
"appearance_palette_locked": payload["appearance_palette_locked"],
|
||||
"appearance_custom_overrides_allowed": payload["appearance_custom_overrides_allowed"],
|
||||
},
|
||||
"settings": payload["settings"],
|
||||
}
|
||||
@@ -1793,21 +1800,28 @@ def write_system_settings(
|
||||
before_privacy = privacy_policy_from_settings(item).model_dump(mode="json")
|
||||
before_maintenance = saved_maintenance_mode(session).as_dict()
|
||||
before_appearance_palette, before_appearance_locked = appearance_settings(item.settings)
|
||||
before_custom_overrides_allowed = appearance_custom_overrides_policy(item.settings) is True
|
||||
appearance_palette_changed = (
|
||||
"appearance_palette" in payload.model_fields_set
|
||||
and payload.appearance_palette is not None
|
||||
and payload.appearance_palette != (before_appearance_palette or "default")
|
||||
)
|
||||
custom_overrides_policy_changed = (
|
||||
"appearance_custom_overrides_allowed" in payload.model_fields_set
|
||||
and payload.appearance_custom_overrides_allowed is not None
|
||||
and payload.appearance_custom_overrides_allowed != before_custom_overrides_allowed
|
||||
)
|
||||
if (
|
||||
(
|
||||
"appearance_palette_locked" in payload.model_fields_set
|
||||
and payload.appearance_palette_locked != before_appearance_locked
|
||||
)
|
||||
or (before_appearance_locked and appearance_palette_changed)
|
||||
or custom_overrides_policy_changed
|
||||
) and not has_scope(principal, "admin:policies:write"):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||
detail="Changing the system appearance lock or its locked value requires admin:policies:write.",
|
||||
detail="Changing the system appearance policy requires admin:policies:write.",
|
||||
)
|
||||
if payload.privacy_retention_policy is not None:
|
||||
privacy_value = payload.privacy_retention_policy.model_dump(mode="json")
|
||||
@@ -1852,6 +1866,14 @@ def write_system_settings(
|
||||
default_palette=payload.appearance_palette or current_palette or "default",
|
||||
palette_locked=payload.appearance_palette_locked if payload.appearance_palette_locked is not None else current_locked,
|
||||
)
|
||||
if (
|
||||
"appearance_custom_overrides_allowed" in payload.model_fields_set
|
||||
and payload.appearance_custom_overrides_allowed is not None
|
||||
):
|
||||
item.settings = update_appearance_custom_overrides_policy(
|
||||
item.settings,
|
||||
allowed=payload.appearance_custom_overrides_allowed,
|
||||
)
|
||||
if "navigation" in payload.model_fields_set:
|
||||
item.settings = update_navigation_preferences(
|
||||
item.settings,
|
||||
|
||||
@@ -53,6 +53,7 @@ class SystemSettingsItem(BaseModel):
|
||||
navigation: NavigationPreferencesPayload | None = None
|
||||
appearance_palette: Literal["default", "civic_blue", "forest", "plum"] = "default"
|
||||
appearance_palette_locked: bool = False
|
||||
appearance_custom_overrides_allowed: bool = False
|
||||
|
||||
|
||||
class SystemSettingsDeltaResponse(BaseModel):
|
||||
@@ -79,6 +80,7 @@ class SystemSettingsUpdateRequest(BaseModel):
|
||||
navigation: NavigationPreferencesPayload | None = None
|
||||
appearance_palette: Literal["default", "civic_blue", "forest", "plum"] | None = None
|
||||
appearance_palette_locked: bool | None = None
|
||||
appearance_custom_overrides_allowed: bool | None = None
|
||||
change_request_id: str | None = None
|
||||
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ manifest = ModuleManifest(
|
||||
id="admin.workspace",
|
||||
title="Use the administration workspace",
|
||||
summary="The administration workspace shows only the sections supplied by enabled modules and allowed by the current account's permissions.",
|
||||
body="System and tenant administration share one workspace. Available sections can include settings, configuration changes and packages, governance templates, groups, and module lifecycle controls. A missing section normally means that its owning module is disabled or the current account lacks the required authority. System appearance settings select a validated palette default. Changing the separate palette lock additionally requires policy-write authority; a system lock suppresses tenant and personal palette choices, while an unlocked default remains inheritable and overridable.",
|
||||
body="System and tenant administration share one workspace. Available sections can include settings, configuration changes and packages, governance templates, groups, and module lifecycle controls. A missing section normally means that its owning module is disabled or the current account lacks the required authority. System appearance settings select a validated palette default. Changing the separate palette lock additionally requires policy-write authority; a system lock suppresses tenant and personal palette choices, while an unlocked default remains inheritable and overridable. The separate advanced-override policy is disabled by default and also requires policy-write authority. Enabling it permits tenants to inherit or narrow access to Core's versioned, accessibility-validated personal accent, surface, and status editor; palette locks continue to win.",
|
||||
documentation_types=("admin", "user"),
|
||||
audience=("tenant_admin", "system_admin", "operator"),
|
||||
metadata={
|
||||
|
||||
@@ -36,6 +36,7 @@ export type SystemSettingsItem = {
|
||||
navigation: NavigationPreferences | null;
|
||||
appearance_palette: UserUiPalette;
|
||||
appearance_palette_locked: boolean;
|
||||
appearance_custom_overrides_allowed: boolean;
|
||||
};
|
||||
|
||||
export type SystemSettingsDeltaSections = Partial<{
|
||||
@@ -46,7 +47,7 @@ export type SystemSettingsDeltaSections = Partial<{
|
||||
maintenance_mode: SystemSettingsItem["maintenance_mode"];
|
||||
settings: SystemSettingsItem["settings"];
|
||||
navigation: SystemSettingsItem["navigation"];
|
||||
appearance: Pick<SystemSettingsItem, "appearance_palette" | "appearance_palette_locked">;
|
||||
appearance: Pick<SystemSettingsItem, "appearance_palette" | "appearance_palette_locked" | "appearance_custom_overrides_allowed">;
|
||||
}>;
|
||||
|
||||
export type SystemSettingsUpdatePayload = {
|
||||
@@ -61,6 +62,7 @@ export type SystemSettingsUpdatePayload = {
|
||||
navigation?: NavigationPreferences | null;
|
||||
appearance_palette?: UserUiPalette;
|
||||
appearance_palette_locked?: boolean;
|
||||
appearance_custom_overrides_allowed?: boolean;
|
||||
change_request_id?: string | null;
|
||||
};
|
||||
|
||||
|
||||
@@ -66,7 +66,8 @@ export default function SystemSettingsPanel({ settings, canWrite, canAccessMaint
|
||||
maintenance_mode: draft.maintenance_mode,
|
||||
navigation: draft.navigation,
|
||||
appearance_palette: draft.appearance_palette,
|
||||
appearance_palette_locked: draft.appearance_palette_locked
|
||||
appearance_palette_locked: draft.appearance_palette_locked,
|
||||
appearance_custom_overrides_allowed: draft.appearance_custom_overrides_allowed
|
||||
});
|
||||
setDraft(saved);
|
||||
setSavedDraft(saved);
|
||||
@@ -120,6 +121,13 @@ export default function SystemSettingsPanel({ settings, canWrite, canAccessMaint
|
||||
help={!canWritePolicy ? "i18n:govoplan-admin.appearance_lock_policy_permission" : undefined}
|
||||
label="i18n:govoplan-admin.lock_system_palette"
|
||||
/>
|
||||
<ToggleSwitch
|
||||
checked={draft.appearance_custom_overrides_allowed}
|
||||
onChange={(appearance_custom_overrides_allowed) => setDraft({ ...draft, appearance_custom_overrides_allowed })}
|
||||
disabled={!canWrite || !canWritePolicy || busy}
|
||||
help={!canWritePolicy ? "i18n:govoplan-admin.appearance_policy_permission" : "i18n:govoplan-admin.custom_overrides_policy_help"}
|
||||
label="i18n:govoplan-admin.allow_custom_appearance_overrides"
|
||||
/>
|
||||
<AppearancePalettePreview palette={draft.appearance_palette} />
|
||||
<p className="muted small-note">i18n:govoplan-admin.system_palette_precedence_help</p>
|
||||
</Card>
|
||||
@@ -156,6 +164,7 @@ function systemSettingsDraftKey(item: SystemSettingsItem): string {
|
||||
maintenance_mode: item.maintenance_mode,
|
||||
navigation: item.navigation,
|
||||
appearance_palette: item.appearance_palette,
|
||||
appearance_palette_locked: item.appearance_palette_locked
|
||||
appearance_palette_locked: item.appearance_palette_locked,
|
||||
appearance_custom_overrides_allowed: item.appearance_custom_overrides_allowed
|
||||
});
|
||||
}
|
||||
|
||||
@@ -41,7 +41,8 @@ export const SYSTEM_SETTINGS_FALLBACK: SystemSettingsItem = {
|
||||
settings: {},
|
||||
navigation: null,
|
||||
appearance_palette: "default",
|
||||
appearance_palette_locked: false
|
||||
appearance_palette_locked: false,
|
||||
appearance_custom_overrides_allowed: false
|
||||
};
|
||||
|
||||
export function applySystemSettingsSections(
|
||||
|
||||
@@ -8,6 +8,9 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-admin.appearance_lock_policy_permission": "Policy-write permission is required to change this lock.",
|
||||
"i18n:govoplan-admin.lock_system_palette": "Lock the system palette",
|
||||
"i18n:govoplan-admin.system_palette_precedence_help": "A system lock suppresses tenant and personal palette choices. Without a lock, explicit child choices take precedence.",
|
||||
"i18n:govoplan-admin.allow_custom_appearance_overrides": "Allow governed personal color overrides",
|
||||
"i18n:govoplan-admin.custom_overrides_policy_help": "Allows tenants to inherit or restrict access to the validated advanced editor. Palette locks still suppress overrides.",
|
||||
"i18n:govoplan-admin.appearance_policy_permission": "Policy-write permission is required to change this appearance policy.",
|
||||
"i18n:govoplan-admin.data_subject_requests.ds001": "Data-subject requests",
|
||||
"i18n:govoplan-admin.data_subject_requests.ds002": "Search, export, and govern erasure requests with explicit provider coverage and retained evidence.",
|
||||
"i18n:govoplan-admin.data_subject_requests.ds003": "Create request",
|
||||
@@ -541,6 +544,9 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-admin.appearance_lock_policy_permission": "Zum Ändern dieser Sperre ist das Recht zum Bearbeiten von Richtlinien erforderlich.",
|
||||
"i18n:govoplan-admin.lock_system_palette": "Systempalette verbindlich festlegen",
|
||||
"i18n:govoplan-admin.system_palette_precedence_help": "Eine Systemsperre unterdrückt Mandanten- und Benutzerauswahlen. Ohne Sperre haben ausdrückliche untergeordnete Auswahlen Vorrang.",
|
||||
"i18n:govoplan-admin.allow_custom_appearance_overrides": "Gesteuerte persönliche Farbanpassungen zulassen",
|
||||
"i18n:govoplan-admin.custom_overrides_policy_help": "Mandanten können den Zugang zum geprüften erweiterten Editor erben oder einschränken. Palettensperren unterdrücken Anpassungen weiterhin.",
|
||||
"i18n:govoplan-admin.appearance_policy_permission": "Zum Ändern dieser Darstellungsrichtlinie ist das Recht zum Bearbeiten von Richtlinien erforderlich.",
|
||||
"i18n:govoplan-admin.data_subject_requests.ds001": "Betroffenenanfragen",
|
||||
"i18n:govoplan-admin.data_subject_requests.ds002": "Auskunfts- und Löschanfragen mit expliziter Anbieterabdeckung und Aufbewahrungsnachweisen suchen, exportieren und steuern.",
|
||||
"i18n:govoplan-admin.data_subject_requests.ds003": "Anfrage anlegen",
|
||||
|
||||
Reference in New Issue
Block a user