feat(P-11): implement legal hold functionality for media assets and update app version to 0.8.86
All checks were successful
Deploy Development / deploy (push) Successful in 39s
Test Suite / pytest-backend (push) Successful in 39s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 10s
Test Suite / playwright-tests (push) Successful in 57s
All checks were successful
Deploy Development / deploy (push) Successful in 39s
Test Suite / pytest-backend (push) Successful in 39s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 10s
Test Suite / playwright-tests (push) Successful in 57s
This commit is contained in:
parent
f79f83e8f9
commit
ee54f8380f
|
|
@ -923,7 +923,8 @@ def enrich_exercise_detail(exercise_id: int, cur) -> dict:
|
|||
"""SELECT em.id, em.media_type, em.file_path, em.file_size, em.mime_type, em.original_filename,
|
||||
em.embed_url, em.embed_platform, em.title, em.description, em.sort_order, em.is_primary, em.context,
|
||||
em.media_asset_id, ma.copyright_notice AS asset_copyright_notice,
|
||||
ma.lifecycle_state AS asset_lifecycle_state
|
||||
ma.lifecycle_state AS asset_lifecycle_state,
|
||||
ma.legal_hold_active AS asset_legal_hold_active
|
||||
FROM exercise_media em
|
||||
LEFT JOIN media_assets ma ON ma.id = em.media_asset_id
|
||||
WHERE em.exercise_id = %s
|
||||
|
|
@ -2466,7 +2467,8 @@ def _fetch_media_row(cur, exercise_id: int, media_id: int) -> Optional[dict]:
|
|||
"""SELECT em.id, em.exercise_id, em.media_type, em.file_path, em.file_size, em.mime_type, em.original_filename,
|
||||
em.embed_url, em.embed_platform, em.title, em.description, em.sort_order, em.is_primary, em.context, em.created_at,
|
||||
em.media_asset_id, ma.storage_key AS asset_storage_key,
|
||||
ma.lifecycle_state AS asset_lifecycle_state
|
||||
ma.lifecycle_state AS asset_lifecycle_state,
|
||||
ma.legal_hold_active AS asset_legal_hold_active
|
||||
FROM exercise_media em
|
||||
LEFT JOIN media_assets ma ON ma.id = em.media_asset_id
|
||||
WHERE em.id = %s AND em.exercise_id = %s""",
|
||||
|
|
@ -2496,6 +2498,12 @@ def download_exercise_media_file(
|
|||
if (media.get("embed_url") or "").strip():
|
||||
raise HTTPException(status_code=400, detail="Embed-Medien haben keine Datei-URL")
|
||||
|
||||
if bool(media.get("asset_legal_hold_active")):
|
||||
raise HTTPException(
|
||||
status_code=451,
|
||||
detail={"code": "LEGAL_HOLD_ACTIVE", "message": "Dieses Medium ist gesperrt und steht nicht zur Verfügung."}
|
||||
)
|
||||
|
||||
lc = (media.get("asset_lifecycle_state") or "active").strip().lower()
|
||||
if lc == "trash_hidden":
|
||||
_assert_can_edit_exercise(cur, exercise_id, tenant)
|
||||
|
|
|
|||
|
|
@ -558,7 +558,7 @@ def _list_main_visibility_where(
|
|||
raise HTTPException(status_code=400, detail="Ungültiger lifecycle-Filter")
|
||||
|
||||
active_sql, active_params = _list_active_visibility_clause(
|
||||
is_plat, profile_id, include_legal_hold=(is_plat or is_sup)
|
||||
is_plat, profile_id, include_legal_hold=is_sup
|
||||
)
|
||||
trash_sql, trash_params = _list_trash_visibility_clause(
|
||||
is_plat, is_sup, profile_id, admin_club_ids
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Shinkan Jinkendo Version Information
|
||||
|
||||
APP_VERSION = "0.8.85"
|
||||
APP_VERSION = "0.8.86"
|
||||
BUILD_DATE = "2026-05-11"
|
||||
DB_SCHEMA_VERSION = "20260511051"
|
||||
|
||||
|
|
@ -15,13 +15,13 @@ MODULE_VERSIONS = {
|
|||
"admin_users": "1.0.0", # GET /api/admin/users
|
||||
"platform_media_storage": "1.0.0", # GET/PUT /api/admin/platform-media-storage (Superadmin-Pfad unter MEDIA_ROOT)
|
||||
"media_rights": "1.3.0", # P-11: write_audit_log_entry + legal_hold_set/released events
|
||||
"media_assets": "1.17.0", # P-11: Legal-Hold-Sichtbarkeitsfilter + Admin-Endpoints (set/release/list)
|
||||
"media_assets": "1.18.0", # P-11: Legal-Hold nur fuer Superadmin sichtbar (nicht fuer alle Plattform-Admins)
|
||||
"media_legal_hold": "1.0.0", # P-11: Sofortsperre-Services (set_legal_hold, release_legal_hold)
|
||||
"media_lifecycle": "1.1.0", # P-11: Retention-Job ueberspringt Legal-Hold-Assets
|
||||
"groups": "0.1.0",
|
||||
"skills": "0.1.0",
|
||||
"methods": "0.1.0",
|
||||
"exercises": "2.22.0", # P-11: assert_not_under_legal_hold bei from-asset Medienverknuepfung
|
||||
"exercises": "2.23.0", # P-11: enrich_exercise_detail + download_file blocken Legal-Hold-Assets (451)
|
||||
"training_units": "0.2.0",
|
||||
"training_programs": "0.1.0",
|
||||
"planning": "0.8.1", # Vorlagen Leseprüfung library_content_visible_to_profile
|
||||
|
|
@ -33,6 +33,17 @@ MODULE_VERSIONS = {
|
|||
}
|
||||
|
||||
CHANGELOG = [
|
||||
{
|
||||
"version": "0.8.86",
|
||||
"date": "2026-05-11",
|
||||
"changes": [
|
||||
"Fix P-11: download_exercise_media_file gibt 451 zurück für Legal-Hold-Assets (Datei nicht mehr auslieferbar).",
|
||||
"Fix P-11: enrich_exercise_detail liefert asset_legal_hold_active im Media-Array (Frontend-Komponenten koennen Hold erkennen).",
|
||||
"Fix P-11: ExerciseMediaEmbed + ExerciseMediaThumbTile zeigen 'Medium nicht verfügbar / Gesperrt' statt Datei laden.",
|
||||
"Fix P-11: ExerciseFormPage Vorschau-Modal zeigt Hinweis statt Datei bei Legal-Hold.",
|
||||
"Fix P-11: Media-Bibliothek-Liste (list_media_assets) schliesst Legal-Hold fuer Plattform-Admins aus — nur Superadmin sieht sie.",
|
||||
],
|
||||
},
|
||||
{
|
||||
"version": "0.8.85",
|
||||
"date": "2026-05-11",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,15 @@ export default function ExerciseMediaEmbed({ exerciseId, media, layoutSize = 'me
|
|||
: { maxWidth: 'min(560px, 85vw)', marginTop: '0.5rem' }
|
||||
|
||||
if (!media || exerciseId == null) return null
|
||||
|
||||
if (media.asset_legal_hold_active) {
|
||||
return (
|
||||
<div style={{ ...box, color: 'var(--danger)', fontSize: '0.85rem', padding: '8px 0' }}>
|
||||
Medium nicht verfügbar (gesperrt)
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (media.embed_url) {
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -11,6 +11,32 @@ export default function ExerciseMediaThumbTile({ exerciseId, media, onOpenPrevie
|
|||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
}
|
||||
|
||||
if (media.asset_legal_hold_active) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
flexShrink: 0,
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden',
|
||||
background: 'rgba(216, 90, 48, 0.10)',
|
||||
border: '1px solid rgba(216, 90, 48, 0.35)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
title: 'Gesperrt',
|
||||
}}
|
||||
title="Medium gesperrt"
|
||||
>
|
||||
<span style={{ fontSize: '11px', color: 'var(--danger)', textAlign: 'center', padding: '4px' }}>
|
||||
Gesperrt
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
role="button"
|
||||
|
|
|
|||
|
|
@ -1676,7 +1676,11 @@ function ExerciseFormPage() {
|
|||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<h3 style={{ marginTop: 0, fontSize: '1.05rem' }}>Vorschau</h3>
|
||||
{mediaPreview.embed_url ? (
|
||||
{mediaPreview.asset_legal_hold_active ? (
|
||||
<p style={{ color: 'var(--danger)', fontSize: '0.9rem' }}>
|
||||
Dieses Medium ist gesperrt und steht nicht zur Verfügung.
|
||||
</p>
|
||||
) : mediaPreview.embed_url ? (
|
||||
<p style={{ fontSize: '14px', wordBreak: 'break-all' }}>
|
||||
<a href={mediaPreview.embed_url} target="_blank" rel="noreferrer">
|
||||
{mediaPreview.embed_url}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Shinkan Jinkendo Frontend Version
|
||||
|
||||
export const APP_VERSION = "0.8.85"
|
||||
export const APP_VERSION = "0.8.86"
|
||||
export const BUILD_DATE = "2026-05-11"
|
||||
|
||||
export const PAGE_VERSIONS = {
|
||||
|
|
@ -23,4 +23,6 @@ export const PAGE_VERSIONS = {
|
|||
MediaLibraryPage: "1.6.0", // P-11: Legal-Hold-Badge, Superadmin-Aktionen, Bestaetigungs-Dialog
|
||||
ExerciseInlineFileMediaModal: "1.1.0", // P-06: RightsDeclarationDialog vor Upload
|
||||
ExerciseInlineEmbedModal: "1.1.0", // P-06: RightsDeclarationDialog vor Embed-Upload
|
||||
ExerciseMediaEmbed: "1.1.0", // P-11: Legal-Hold-Placeholder statt Datei
|
||||
ExerciseMediaThumbTile: "1.1.0", // P-11: Legal-Hold-Kachel statt Datei-Vorschau
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user