diff --git a/src/main/java/de/tum/cit/aet/artemis/core/config/PublicResourcesConfiguration.java b/src/main/java/de/tum/cit/aet/artemis/core/config/PublicResourcesConfiguration.java index 2d7adf676f74..e39b8915c94f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/core/config/PublicResourcesConfiguration.java +++ b/src/main/java/de/tum/cit/aet/artemis/core/config/PublicResourcesConfiguration.java @@ -10,6 +10,7 @@ import jakarta.validation.constraints.NotNull; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.http.CacheControl; @@ -32,6 +33,9 @@ public PublicResourcesConfiguration(JHipsterProperties jHipsterProperties) { this.jHipsterProperties = jHipsterProperties; } + @Value("${artemis.file-upload-path}") + private String fileUploadPath; + @Override public void addResourceHandlers(@NotNull ResourceHandlerRegistry registry) { // Enable static resource serving in general from "/public" from both classpath and hosts filesystem @@ -46,6 +50,16 @@ public void addResourceHandlers(@NotNull ResourceHandlerRegistry registry) { addResourceHandlerForPath(registry, "images", "about").setCacheControl(defaultCacheControl); addResourceHandlerForPath(registry, "emoji").setCacheControl(defaultCacheControl); + + // Add caching for course icons, user profile pictures, and drag and drop quiz pictures + // Add resource handlers for dynamic image paths based on fileUploadPath + // TODO: those paths have to be the same as in FilePathService, ideally we reuse the constants and define them only once + registry.addResourceHandler("/images/course/icons/**").addResourceLocations("file:" + fileUploadPath + "/images/course/icons/").setCacheControl(defaultCacheControl); + + registry.addResourceHandler("/images/user/profile-pictures/**").addResourceLocations("file:" + fileUploadPath + "/images/user/profile-pictures/") + .setCacheControl(defaultCacheControl); + + registry.addResourceHandler("/images/drag-and-drop/**").addResourceLocations("file:" + fileUploadPath + "/images/drag-and-drop/").setCacheControl(defaultCacheControl); } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/core/web/FileResource.java b/src/main/java/de/tum/cit/aet/artemis/core/web/FileResource.java index 4413ec2f6a8d..e8ad0e1fc5fe 100644 --- a/src/main/java/de/tum/cit/aet/artemis/core/web/FileResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/core/web/FileResource.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -646,7 +647,8 @@ private ResponseEntity responseEntityForFilePath(Path filePath) { if (file == null) { return ResponseEntity.notFound().build(); } - return ResponseEntity.ok(file); + return ResponseEntity.ok().cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS)) // Cache for 30 days; + .contentType(getMediaTypeFromFilename(filePath.getFileName().toString())).body(file); } catch (IOException e) { log.error("Failed to return requested file with path {}", filePath, e); diff --git a/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts b/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts index e4b11dd87612..2590612104b4 100644 --- a/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts +++ b/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts @@ -136,6 +136,7 @@ export class ConversationMessagesComponent implements OnInit, AfterViewInit, OnD this.createEmptyPost(); } } + private subscribeToMetis() { this.metisService.posts.pipe(takeUntil(this.ngUnsubscribe)).subscribe((posts: Post[]) => { this.setPosts(posts); diff --git a/src/main/webapp/app/shared/metis/metis-conversation.service.ts b/src/main/webapp/app/shared/metis/metis-conversation.service.ts index 012ff7aa1565..b21b4074294d 100644 --- a/src/main/webapp/app/shared/metis/metis-conversation.service.ts +++ b/src/main/webapp/app/shared/metis/metis-conversation.service.ts @@ -116,7 +116,7 @@ export class MetisConversationService implements OnDestroy { public setActiveConversation(conversationIdentifier: ConversationDTO | number | undefined) { this.updateLastReadDateAndNumberOfUnreadMessages(); - let cachedConversation = undefined; + let cachedConversation: ConversationDTO | undefined = undefined; if (conversationIdentifier) { const parameterJustId = typeof conversationIdentifier === 'number'; cachedConversation = this.conversationsOfUser.find( diff --git a/src/test/java/de/tum/cit/aet/artemis/core/util/CourseTestService.java b/src/test/java/de/tum/cit/aet/artemis/core/util/CourseTestService.java index 3b8ce269954b..c80195c2b8aa 100644 --- a/src/test/java/de/tum/cit/aet/artemis/core/util/CourseTestService.java +++ b/src/test/java/de/tum/cit/aet/artemis/core/util/CourseTestService.java @@ -3303,8 +3303,7 @@ private Course createCourseWithCourseImageAndReturn() throws Exception { course = objectMapper.readValue(result.getResponse().getContentAsString(), Course.class); assertThat(course.getCourseIcon()).as("Course icon got stored").isNotNull(); - var imgResult = request.performMvcRequest(get(course.getCourseIcon())).andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM)) - .andReturn(); + var imgResult = request.performMvcRequest(get(course.getCourseIcon())).andExpect(status().isOk()).andExpect(content().contentType(MediaType.IMAGE_PNG)).andReturn(); assertThat(imgResult.getResponse().getContentAsByteArray()).isNotEmpty(); var createdCourse = courseRepo.findByIdElseThrow(course.getId()); diff --git a/src/test/java/de/tum/cit/aet/artemis/quiz/QuizExerciseIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/quiz/QuizExerciseIntegrationTest.java index b11c688358fa..e787ee1533a0 100644 --- a/src/test/java/de/tum/cit/aet/artemis/quiz/QuizExerciseIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/quiz/QuizExerciseIntegrationTest.java @@ -306,7 +306,8 @@ private void checkCreatedFiles(QuizExercise quizExercise) throws Exception { } private void checkCreatedFile(String path) throws Exception { - MvcResult result = request.performMvcRequest(get(path)).andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM)).andReturn(); + MediaType mediaType = path.endsWith(".png") ? MediaType.IMAGE_PNG : MediaType.IMAGE_JPEG; + MvcResult result = request.performMvcRequest(get(path)).andExpect(status().isOk()).andExpect(content().contentType(mediaType)).andReturn(); byte[] image = result.getResponse().getContentAsByteArray(); assertThat(image).isNotEmpty(); }