feat(webui): complete files quick access
This commit is contained in:
@@ -686,10 +686,11 @@ manifest = ModuleManifest(
|
||||
summary="Use managed files in the Records and documents area and keep a compact file surface beside current work.",
|
||||
body=(
|
||||
"Files contributes its authorized workspace to Records and documents. When Quick Access is enabled, the owner-rendered "
|
||||
"recent-file selector appears in Files on the right rail and can return one exact file-version reference to the current task. "
|
||||
"Opening the selector causes Files to re-run its own authorization; completion and cancellation are explicit, and the full Files "
|
||||
"workspace remains available as a deep-link fallback. View and rail settings may recommend or focus this tool, but do not bypass "
|
||||
"file ownership, shares, connector policy, integrity gates, or purpose-aware access."
|
||||
"selector shows at most seven recently changed files and can return one exact file-version reference to the current task. "
|
||||
"Accounts with upload permission can launch the full Files upload dialog into a freshly reauthorized managed space. Opening "
|
||||
"either path causes Files to re-run its own provider, space, folder, object, and scope checks; completion and cancellation are "
|
||||
"explicit, and the full Files workspace remains available as a deep-link fallback. View and rail settings may recommend or "
|
||||
"focus this tool, but do not bypass file ownership, shares, connector policy, integrity gates, or purpose-aware access."
|
||||
),
|
||||
layer="configured",
|
||||
documentation_types=("user", "admin"),
|
||||
@@ -715,11 +716,11 @@ manifest = ModuleManifest(
|
||||
"summary": "Verwaltete Dateien im Produktbereich Akten und Dokumente und optional neben der aktuellen Arbeit verwenden.",
|
||||
"body": (
|
||||
"Files ordnet seinen berechtigten Arbeitsbereich Akten und Dokumente zu. Ist der Schnellzugriff aktiviert, erscheint "
|
||||
"die vom Modul gerenderte Auswahl zuletzt berechtigter Dateien rechts neben der aktuellen Seite und kann einen exakten "
|
||||
"Dateiversionsverweis an die aktuelle Aufgabe zurückgeben. Files prüft die Berechtigung beim Öffnen erneut; Abschluss und "
|
||||
"Abbruch sind ausdrücklich, und der vollständige Files-Arbeitsbereich bleibt als Ausweichziel erreichbar. Ansichts- und "
|
||||
"Leistenkonfiguration dürfen das Werkzeug empfehlen oder fokussieren, umgehen aber weder Eigentum, Freigaben, Connector-"
|
||||
"Richtlinien, Integritätsprüfungen noch zweckgebundenen Zugriff."
|
||||
"die vom Modul gerenderte Auswahl von höchstens sieben zuletzt geänderten berechtigten Dateien rechts neben der aktuellen "
|
||||
"Seite und kann einen exakten Dateiversionsverweis zurückgeben. Mit Upload-Berechtigung lässt sich der vollständige "
|
||||
"Upload-Dialog in einem erneut berechtigungsgeprüften verwalteten Bereich öffnen. Files prüft Anbieter, Bereich, Ordner, "
|
||||
"Objekt und Berechtigung bei jedem Pfad erneut. Abschluss und Abbruch sind ausdrücklich; Eigentum, Freigaben, Connector-"
|
||||
"Richtlinien, Integritätsprüfungen und zweckgebundener Zugriff bleiben maßgeblich."
|
||||
),
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from govoplan_core.auth import ApiPrincipal, require_scope
|
||||
@@ -14,6 +14,7 @@ from govoplan_files.backend.storage.files import (
|
||||
count_assets_for_user,
|
||||
list_assets_for_user,
|
||||
list_assets_for_user_window,
|
||||
list_recent_assets_for_user,
|
||||
)
|
||||
|
||||
|
||||
@@ -79,6 +80,7 @@ def list_files(
|
||||
path_prefix: str | None = None,
|
||||
campaign_usage: Literal["linked", "unlinked"] | None = None,
|
||||
audit_relevant: bool | None = None,
|
||||
sort: Literal["path", "recent"] = "path",
|
||||
page_size: int | None = Query(default=None, ge=1, le=1000),
|
||||
cursor: str | None = None,
|
||||
session: Session = Depends(get_session),
|
||||
@@ -99,6 +101,31 @@ def list_files(
|
||||
audit_relevant=audit_relevant,
|
||||
is_admin=_is_admin(principal),
|
||||
)
|
||||
if sort == "recent":
|
||||
if cursor:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||
detail="Recent file projections do not accept a path-order cursor.",
|
||||
)
|
||||
recent_limit = page_size or 25
|
||||
assets = list_recent_assets_for_user(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
user_id=principal.user.id,
|
||||
limit=recent_limit,
|
||||
owner_type=owner_type,
|
||||
owner_id=owner_id,
|
||||
campaign_id=campaign_id,
|
||||
path_prefix=path_prefix,
|
||||
campaign_usage=campaign_usage,
|
||||
audit_relevant=audit_relevant,
|
||||
is_admin=_is_admin(principal),
|
||||
)
|
||||
return FileListResponse(
|
||||
files=_asset_list_response(session, assets, include_shares=True),
|
||||
total=total,
|
||||
watermark=watermark,
|
||||
)
|
||||
effective_page_size = _cursor_page_size(FILES_LIST_CURSOR_SCOPE, cursor, page_size)
|
||||
if effective_page_size is not None:
|
||||
fingerprint = _files_list_fingerprint(
|
||||
|
||||
@@ -529,6 +529,41 @@ def list_assets_for_user(
|
||||
return query.order_by(FileAsset.display_path.asc(), FileAsset.updated_at.desc(), FileAsset.id.asc()).all()
|
||||
|
||||
|
||||
def list_recent_assets_for_user(
|
||||
session: Session,
|
||||
*,
|
||||
tenant_id: str,
|
||||
user_id: str,
|
||||
limit: int,
|
||||
owner_type: str | None = None,
|
||||
owner_id: str | None = None,
|
||||
campaign_id: str | None = None,
|
||||
path_prefix: str | None = None,
|
||||
campaign_usage: str | None = None,
|
||||
audit_relevant: bool | None = None,
|
||||
is_admin: bool = False,
|
||||
) -> list[FileAsset]:
|
||||
"""Return a bounded recent projection through the normal access query."""
|
||||
|
||||
query = _asset_visibility_query_for_user(
|
||||
session,
|
||||
tenant_id=tenant_id,
|
||||
user_id=user_id,
|
||||
owner_type=owner_type,
|
||||
owner_id=owner_id,
|
||||
campaign_id=campaign_id,
|
||||
path_prefix=path_prefix,
|
||||
campaign_usage=campaign_usage,
|
||||
audit_relevant=audit_relevant,
|
||||
is_admin=is_admin,
|
||||
)
|
||||
return (
|
||||
query.order_by(FileAsset.updated_at.desc(), FileAsset.id.asc())
|
||||
.limit(max(1, limit))
|
||||
.all()
|
||||
)
|
||||
|
||||
|
||||
def list_assets_for_user_window(
|
||||
session: Session,
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user