diff --git a/src/main/java/backendlab/team4you/casefile/CaseFileController.java b/src/main/java/backendlab/team4you/casefile/CaseFileController.java index 7a1542a..23459e8 100644 --- a/src/main/java/backendlab/team4you/casefile/CaseFileController.java +++ b/src/main/java/backendlab/team4you/casefile/CaseFileController.java @@ -10,6 +10,8 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; +import org.springframework.http.InvalidMediaTypeException; + import java.io.IOException; import java.io.InputStream; @@ -65,7 +67,10 @@ public ResponseEntity downloadFile( MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM; if (caseFile.getContentType() != null && !caseFile.getContentType().isBlank()) { - mediaType = MediaType.parseMediaType(caseFile.getContentType()); + try { + mediaType = MediaType.parseMediaType(caseFile.getContentType()); + } catch (InvalidMediaTypeException e) { + } } StreamingResponseBody body = outputStream -> { @@ -86,6 +91,44 @@ public ResponseEntity downloadFile( .body(body); } + @GetMapping("/{fileId}/preview") + public ResponseEntity previewFile( + @PathVariable Long caseRecordId, + @PathVariable Long fileId, + Principal principal + ) { + UserEntity currentUser = userService.getCurrentUser(principal); + CaseFile caseFile = caseFileService.getCaseFileForViewer(caseRecordId, fileId, currentUser); + + MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM; + if (caseFile.getContentType() != null && !caseFile.getContentType().isBlank()) { + try { + mediaType = MediaType.parseMediaType(caseFile.getContentType()); + } catch (InvalidMediaTypeException e) { + } + } + + StreamingResponseBody body = outputStream -> { + try (InputStream stream = caseFileService.downloadFile(caseRecordId, fileId, currentUser)) { + stream.transferTo(outputStream); + } + }; + + boolean safeInline = isSafeInlinePreview(mediaType); + + ContentDisposition disposition = (safeInline + ? ContentDisposition.inline() + : ContentDisposition.attachment()) + .filename(caseFile.getOriginalFilename(), StandardCharsets.UTF_8) + .build(); + + return ResponseEntity.ok() + .header("X-Content-Type-Options", "nosniff") + .header(HttpHeaders.CONTENT_DISPOSITION, disposition.toString()) + .contentType(mediaType) + .body(body); + } + @DeleteMapping("/{fileId}") public ResponseEntity deleteFile( @PathVariable Long caseRecordId, @@ -96,4 +139,10 @@ public ResponseEntity deleteFile( caseFileService.deleteFile(caseRecordId, fileId, currentUser); return ResponseEntity.noContent().build(); } + + private boolean isSafeInlinePreview(MediaType mediaType) { + return mediaType.isCompatibleWith(MediaType.APPLICATION_PDF) + || "image".equalsIgnoreCase(mediaType.getType()) + || mediaType.isCompatibleWith(MediaType.TEXT_PLAIN); + } } diff --git a/src/main/java/backendlab/team4you/casefile/ui/CaseFileViewController.java b/src/main/java/backendlab/team4you/casefile/ui/CaseFileViewController.java index 378d92c..5a2c028 100644 --- a/src/main/java/backendlab/team4you/casefile/ui/CaseFileViewController.java +++ b/src/main/java/backendlab/team4you/casefile/ui/CaseFileViewController.java @@ -37,6 +37,17 @@ public String caseFiles(@PathVariable Long caseId, Model model, Principal princi return "fragments/case-management/case-file-list :: caseFileList"; } + @GetMapping("/case-records/{caseId}/files/{fileId}/preview-frame") + public String previewFrame( + @PathVariable Long caseId, + @PathVariable Long fileId, + Model model + ) { + model.addAttribute("caseId", caseId); + model.addAttribute("fileId", fileId); + return "fragments/case-management/file-preview-frame :: frame"; + } + @PostMapping("/case-records/{caseId}/files") public String uploadCaseFile( @PathVariable Long caseId, diff --git a/src/main/java/backendlab/team4you/config/SecurityConfig.java b/src/main/java/backendlab/team4you/config/SecurityConfig.java index 46da2f5..5fa903f 100644 --- a/src/main/java/backendlab/team4you/config/SecurityConfig.java +++ b/src/main/java/backendlab/team4you/config/SecurityConfig.java @@ -31,6 +31,7 @@ SecurityFilterChain securityFilterChain(HttpSecurity http, CustomAuthenticationSuccessHandler successHandler) throws Exception { return http + .headers(headers -> headers.frameOptions(frame -> frame.sameOrigin())) .csrf(csrf -> csrf.ignoringRequestMatchers("/webauthn/**", "/api/files/**")) .authorizeHttpRequests( authorizeHttp -> authorizeHttp diff --git a/src/main/resources/templates/fragments/admin-meetings.html b/src/main/resources/templates/fragments/admin-meetings.html index d9dcee3..91c610a 100644 --- a/src/main/resources/templates/fragments/admin-meetings.html +++ b/src/main/resources/templates/fragments/admin-meetings.html @@ -17,9 +17,9 @@

Skapa sammanträde

class="meeting-form" > + th:if="${_csrf != null}" + th:name="${_csrf.parameterName}" + th:value="${_csrf.token}">
+ th:if="${_csrf != null}" + th:name="${_csrf.parameterName}" + th:value="${_csrf.token}">
@@ -192,9 +193,9 @@

Redigera sammanträde

class="delete-meeting-form" > + th:if="${_csrf != null}" + th:name="${_csrf.parameterName}" + th:value="${_csrf.token}"> @@ -209,9 +210,9 @@

Lägg till ärende

class="meeting-form" > + th:if="${_csrf != null}" + th:name="${_csrf.parameterName}" + th:value="${_csrf.token}">
+ th:if="${_csrf != null}" + th:name="${_csrf.parameterName}" + th:value="${_csrf.token}"> @@ -262,9 +263,9 @@

Dagordning

hx-swap="innerHTML" > + th:if="${_csrf != null}" + th:name="${_csrf.parameterName}" + th:value="${_csrf.token}"> @@ -274,9 +275,9 @@

Dagordning

hx-swap="innerHTML" > + th:if="${_csrf != null}" + th:name="${_csrf.parameterName}" + th:value="${_csrf.token}">
@@ -294,15 +295,20 @@
Valda beslutsunderlag
tjansteskrivelse.pdf + +
+ th:if="${_csrf != null}" + th:name="${_csrf.parameterName}" + th:value="${_csrf.token}">
@@ -320,15 +326,20 @@
Tillgängliga handlingar
budgetunderlag.pdf + +
+ th:if="${_csrf != null}" + th:name="${_csrf.parameterName}" + th:value="${_csrf.token}"> @@ -343,4 +354,28 @@
Tillgängliga handlingar
+ + +
\ No newline at end of file diff --git a/src/main/resources/templates/fragments/case-management/case-file-list.html b/src/main/resources/templates/fragments/case-management/case-file-list.html index afd9fb2..6a159db 100644 --- a/src/main/resources/templates/fragments/case-management/case-file-list.html +++ b/src/main/resources/templates/fragments/case-management/case-file-list.html @@ -39,8 +39,8 @@

Filer

  • - KS26-1-1 + KS26-1-1 dokument.pdf @@ -56,10 +56,16 @@

    Filer

    Ladda ner + + - Ingen behörighet - + Ingen behörighet +
  • + + +
    \ No newline at end of file diff --git a/src/main/resources/templates/fragments/case-management/file-preview-frame.html b/src/main/resources/templates/fragments/case-management/file-preview-frame.html new file mode 100644 index 0000000..0dc9125 --- /dev/null +++ b/src/main/resources/templates/fragments/case-management/file-preview-frame.html @@ -0,0 +1,8 @@ +
    + +
    \ No newline at end of file