diff --git a/src/main/java/backendlab/team4you/config/SecurityConfig.java b/src/main/java/backendlab/team4you/config/SecurityConfig.java index dcf9c160..4145846e 100644 --- a/src/main/java/backendlab/team4you/config/SecurityConfig.java +++ b/src/main/java/backendlab/team4you/config/SecurityConfig.java @@ -43,6 +43,11 @@ SecurityFilterChain securityFilterChain(HttpSecurity http, .requestMatchers(HttpMethod.GET, "/api/files/download/**").hasRole(ADMIN) .requestMatchers(HttpMethod.DELETE, "/api/files/delete/**").hasRole(ADMIN) + .requestMatchers(HttpMethod.POST, "/case-officer/cases/**").hasRole(CASE_OFFICER) + .requestMatchers(HttpMethod.GET, "/case-officer/cases/**").hasRole(CASE_OFFICER) + .requestMatchers(HttpMethod.DELETE, "/case-officer/cases/**").hasRole(CASE_OFFICER) + + .requestMatchers("/webauthn/**").hasAnyRole("USER", ADMIN, CASE_OFFICER) .requestMatchers("/admin/**").hasRole(ADMIN) .requestMatchers("/case-officer/**").hasRole(CASE_OFFICER) diff --git a/src/main/java/backendlab/team4you/controller/CaseOfficerController.java b/src/main/java/backendlab/team4you/controller/CaseOfficerController.java index f63d86db..542d795d 100644 --- a/src/main/java/backendlab/team4you/controller/CaseOfficerController.java +++ b/src/main/java/backendlab/team4you/controller/CaseOfficerController.java @@ -1,25 +1,35 @@ package backendlab.team4you.controller; +import backendlab.team4you.casefile.CaseFile; +import backendlab.team4you.casefile.CaseFileListItemDto; +import backendlab.team4you.casefile.CaseFileService; import backendlab.team4you.caserecord.CaseRecord; import backendlab.team4you.caserecord.CaseRecordRepository; import backendlab.team4you.caserecord.CaseRecordService; import backendlab.team4you.caserecord.CaseStatus; +import backendlab.team4you.common.ConfidentialityLevel; import backendlab.team4you.exceptions.CaseRecordNotFoundException; import backendlab.team4you.exceptions.UserNotFoundException; import backendlab.team4you.user.UserEntity; import backendlab.team4you.user.UserRepository; +import backendlab.team4you.user.UserService; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -import org.springframework.http.HttpStatus; +import org.springframework.http.*; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.security.Principal; +import java.util.List; @Controller @PreAuthorize("hasRole('CASE_OFFICER')") @@ -27,10 +37,14 @@ public class CaseOfficerController { private final CaseRecordRepository caseRecordRepository; private final UserRepository userRepository; + private final UserService userService; + private final CaseFileService caseFileService; - public CaseOfficerController(CaseRecordRepository caseRecordRepository, UserRepository userRepository) { + public CaseOfficerController(CaseRecordRepository caseRecordRepository, UserRepository userRepository, UserService userService, CaseFileService caseFileService) { this.caseRecordRepository = caseRecordRepository; this.userRepository = userRepository; + this.userService = userService; + this.caseFileService = caseFileService; } @GetMapping("/case-officer") @@ -41,6 +55,7 @@ public String caseOfficerHome() { @GetMapping("/case-officer/cases") public String listCases( @RequestParam(defaultValue = "0") int page, + @RequestHeader(value = "HX-Request", required = false) String htmxRequest, Authentication auth, Model model ) { @@ -54,7 +69,10 @@ public String listCases( model.addAttribute("currentPage", page); model.addAttribute("totalPages", cases.getTotalPages()); - return "fragments/case-officer-cases :: content"; + if (htmxRequest != null) { + return "fragments/case-officer-cases :: content"; + } + return "case-officer-cases"; } @PostMapping("/case-officer/cases/close") @@ -75,4 +93,85 @@ public String closeCase(@RequestParam String id, Authentication auth) { return ""; } + @GetMapping("/case-officer/cases/{caseRecordId}/files") + public String getCaseFiles(@PathVariable Long caseRecordId, Principal principal, Model model) { + UserEntity currentUser = userService.getCurrentUser(principal); + List files = caseFileService.listFileItemsForViewer(caseRecordId, currentUser); + + model.addAttribute("files", files); + model.addAttribute("caseRecordId", caseRecordId); + + return "fragments/case-management/case-file-list-officer :: caseFileList"; + } + + + @PostMapping("/case-officer/cases/{caseRecordId}/files") + public String uploadFile( + @PathVariable Long caseRecordId, + @RequestParam("file") MultipartFile file, + @RequestParam("confidentialityLevel") ConfidentialityLevel confidentialityLevel, + Principal principal, + Model model + ) { + UserEntity currentUser = userService.getCurrentUser(principal); + + try { + caseFileService.uploadFile(caseRecordId, file, confidentialityLevel, currentUser); + model.addAttribute("successMessage", "Filen laddades upp"); + } catch (Exception e) { + model.addAttribute("errorMessage", "Kunde inte ladda upp filen: " + e.getMessage()); + } + + return getCaseFiles(caseRecordId, principal, model); + } + + @GetMapping("/case-officer/cases/{caseRecordId}/files/{fileId}") + public ResponseEntity downloadFile( + @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()) { + mediaType = MediaType.parseMediaType(caseFile.getContentType()); + } + + StreamingResponseBody body = outputStream -> { + try (InputStream stream = caseFileService.downloadFile(caseRecordId, fileId, currentUser)) { + stream.transferTo(outputStream); + } + }; + + return ResponseEntity.ok() + .header( + HttpHeaders.CONTENT_DISPOSITION, + ContentDisposition.attachment() + .filename(caseFile.getOriginalFilename(), StandardCharsets.UTF_8) + .build() + .toString() + ) + .contentType(mediaType) + .body(body); + } + + @DeleteMapping("/case-officer/cases/{caseRecordId}/files/{fileId}") + public String deleteFile( + @PathVariable Long caseRecordId, + @PathVariable Long fileId, + Principal principal, + Model model + ) { + + UserEntity currentUser = userService.getCurrentUser(principal); + try { + caseFileService.deleteFile(caseRecordId, fileId, currentUser); + model.addAttribute("successMessage", "Filen togs bort"); + } catch (Exception e) { + model.addAttribute("errorMessage", "Kunde inte ta bort filen: " + e.getMessage()); + } + return getCaseFiles(caseRecordId, principal, model); + } } diff --git a/src/main/resources/templates/case-officer-layout.html b/src/main/resources/templates/case-officer-layout.html index 30d17265..7b217949 100644 --- a/src/main/resources/templates/case-officer-layout.html +++ b/src/main/resources/templates/case-officer-layout.html @@ -10,7 +10,8 @@ - + + @@ -23,7 +24,17 @@

Välkommen Handläggare

- + + + diff --git a/src/main/resources/templates/fragments/case-management/case-file-list-officer.html b/src/main/resources/templates/fragments/case-management/case-file-list-officer.html new file mode 100644 index 00000000..b5abf039 --- /dev/null +++ b/src/main/resources/templates/fragments/case-management/case-file-list-officer.html @@ -0,0 +1,78 @@ +
+ +

Filer

+ + + + + +
+ + + +
+ + +
+ +
+ + +
+ + +
+ +
    +
  • Inga filer uppladdade ännu.
  • + +
  • +
    + KS26-1-1 + + dokument.pdf + + 🔒 +
    + +
    + + Ladda ner + + + + Ingen behörighet + + + +
    +
  • +
+
diff --git a/src/main/resources/templates/fragments/case-officer-cases.html b/src/main/resources/templates/fragments/case-officer-cases.html index a093c9a3..79841541 100644 --- a/src/main/resources/templates/fragments/case-officer-cases.html +++ b/src/main/resources/templates/fragments/case-officer-cases.html @@ -25,9 +25,8 @@

Mina ärenden

-
Beskrivning
-

-

+

Beskrivning

+

Registry: KS

@@ -35,17 +34,29 @@
Beskrivning

Ägare: Admin Name

-
- + + + diff --git a/src/main/resources/templates/fragments/case-officer-sidenav.html b/src/main/resources/templates/fragments/case-officer-sidenav.html index 568c8995..be25cc7e 100644 --- a/src/main/resources/templates/fragments/case-officer-sidenav.html +++ b/src/main/resources/templates/fragments/case-officer-sidenav.html @@ -9,7 +9,9 @@
  • + hx-target="#content-area" + hx-swap="innerHTML" + hx-push-url="true"> Hantera ärenden