Skip to content

Commit

Permalink
Allow file upload to course/conversation
Browse files Browse the repository at this point in the history
  • Loading branch information
pzdr7 committed Oct 24, 2024
1 parent 65e33ba commit a17ee25
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ public static Path getMarkdownFilePath() {
return Path.of(fileUploadPath, "markdown");
}

public static Path getMarkdownFilePathForConversation(long courseId, long conversationId) {
return getMarkdownFilePath().resolve("communication").resolve(String.valueOf(courseId)).resolve(String.valueOf(conversationId));
}

/**
* Convert the given public file url to its corresponding local path
*
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/de/tum/cit/aet/artemis/core/service/FileService.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,24 @@ public URI handleSaveFile(MultipartFile file, boolean keepFilename, boolean mark
return URI.create(markdown ? MARKDOWN_FILE_SUBPATH : DEFAULT_FILE_SUBPATH).resolve(currentFilename);
}

public URI handleSaveFileInConversation(MultipartFile file, Long courseId, Long conversationId) {
// TODO: access check? course is already checked, but the user might not be a member of the conversation
String filename = checkAndSanitizeFilename(file.getOriginalFilename());

validateExtension(filename, true);

final String filenamePrefix = "Markdown_";
final Path path = FilePathService.getMarkdownFilePathForConversation(courseId, conversationId);

String fileName = generateFilename(filenamePrefix, filename, false); // TODO: keep?
Path filePath = path.resolve(fileName);

copyFile(file, filePath);

String currentFilename = filePath.getFileName().toString();
return URI.create("/api/files/courses/" + courseId + "/conversations/" + conversationId + "/").resolve(currentFilename);
}

/**
* Saves a file to the given path using a generated filename.
*
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/de/tum/cit/aet/artemis/core/web/FileResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastStudent;
import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastTutor;
import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInCourse.EnforceAtLeastEditorInCourse;
import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInCourse.EnforceAtLeastStudentInCourse;
import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService;
import de.tum.cit.aet.artemis.core.service.FilePathService;
import de.tum.cit.aet.artemis.core.service.FileService;
Expand Down Expand Up @@ -163,6 +164,28 @@ public ResponseEntity<String> saveMarkdownFile(@RequestParam(value = "file") Mul
return ResponseEntity.created(new URI(responsePath)).body(responseBody);
}

@PostMapping("files/courses/{courseId}/conversations/{conversationId}")
@EnforceAtLeastStudentInCourse
public ResponseEntity<String> saveMarkdownFileForConversation(@RequestParam(value = "file") MultipartFile file, @PathVariable Long courseId, @PathVariable Long conversationId)
throws URISyntaxException {
log.debug("REST request to upload file for markdown in conversation: {} for conversation {} in course {}", file.getOriginalFilename(), conversationId, courseId);
String responsePath = fileService.handleSaveFileInConversation(file, courseId, conversationId).toString();

// return path for getting the file
String responseBody = "{\"path\":\"" + responsePath + "\"}";

return ResponseEntity.created(new URI(responsePath)).body(responseBody);

Check warning

Code scanning / CodeQL

Cross-site scripting Medium

Cross-site scripting vulnerability due to a
user-provided value
.
Cross-site scripting vulnerability due to a
user-provided value
.
}

@GetMapping("files/courses/{courseId}/conversations/{conversationId}/{filename}")
@EnforceAtLeastStudentInCourse
public ResponseEntity<byte[]> getMarkdownFileForConversation(@PathVariable Long courseId, @PathVariable Long conversationId, @PathVariable String filename) {
// TODO: access check
log.debug("REST request to get file for markdown in conversation: File {} for conversation {} in course {}", filename, conversationId, courseId);
sanitizeFilenameElseThrow(filename);
return buildFileResponse(FilePathService.getMarkdownFilePathForConversation(courseId, conversationId), filename);
}

/**
* GET /files/markdown/:filename : Get the markdown file with the given filename
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
Signal,
ViewChild,
computed,
inject,
input,
} from '@angular/core';
import { MonacoEditorComponent } from 'app/shared/monaco-editor/monaco-editor.component';
import { NgbNavChangeEvent } from '@ng-bootstrap/ng-bootstrap';
Expand Down Expand Up @@ -45,6 +47,7 @@ import { SafeHtml } from '@angular/platform-browser';
import { ArtemisMarkdownService } from 'app/shared/markdown.service';
import { parseMarkdownForDomainActions } from 'app/shared/markdown-editor/monaco/markdown-editor-parsing.helper';
import { COMMUNICATION_MARKDOWN_EDITOR_OPTIONS, DEFAULT_MARKDOWN_EDITOR_OPTIONS } from 'app/shared/monaco-editor/monaco-editor-option.helper';
import { MetisService } from 'app/shared/metis/metis.service';

export enum MarkdownEditorHeight {
INLINE = 100,
Expand Down Expand Up @@ -86,6 +89,12 @@ const BORDER_HEIGHT_OFFSET = 2;
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MarkdownEditorMonacoComponent implements AfterContentInit, AfterViewInit, OnDestroy {
private readonly alertService = inject(AlertService);
// We inject the MetisService here to avoid a NullInjectorError in the FileUploaderService.
private readonly metisService = inject(MetisService);
private readonly fileUploaderService = inject(FileUploaderService);
private readonly artemisMarkdown = inject(ArtemisMarkdownService);

@ViewChild(MonacoEditorComponent, { static: false }) monacoEditor: MonacoEditorComponent;
@ViewChild('fullElement', { static: true }) fullElement: ElementRef<HTMLDivElement>;
@ViewChild('wrapper', { static: true }) wrapper: ElementRef<HTMLDivElement>;
Expand Down Expand Up @@ -175,6 +184,8 @@ export class MarkdownEditorMonacoComponent implements AfterContentInit, AfterVie
@Input()
metaActions: TextEditorAction[] = [new FullscreenAction()];

readonly useCommunicationForFileUpload = input<boolean>(false);

@Output()
markdownChange = new EventEmitter<string>();

Expand Down Expand Up @@ -245,11 +256,7 @@ export class MarkdownEditorMonacoComponent implements AfterContentInit, AfterVie
protected readonly TAB_PREVIEW = MarkdownEditorMonacoComponent.TAB_PREVIEW;
protected readonly TAB_VISUAL = MarkdownEditorMonacoComponent.TAB_VISUAL;

constructor(
private alertService: AlertService,
private fileUploaderService: FileUploaderService,
private artemisMarkdown: ArtemisMarkdownService,
) {
constructor() {
this.uniqueMarkdownEditorId = 'markdown-editor-' + uuid();
}

Expand Down Expand Up @@ -396,6 +403,8 @@ export class MarkdownEditorMonacoComponent implements AfterContentInit, AfterVie
this.onContentHeightChanged(this.monacoEditor.getContentHeight());
const editorHeight = this.getEditorHeight();
this.monacoEditor.layoutWithFixedSize(this.getEditorWidth(), editorHeight);
// Prevents an issue with line wraps in the editor
this.monacoEditor.layout();
}

/**
Expand Down Expand Up @@ -463,7 +472,10 @@ export class MarkdownEditorMonacoComponent implements AfterContentInit, AfterVie
*/
embedFiles(files: File[]): void {
files.forEach((file) => {
this.fileUploaderService.uploadMarkdownFile(file).then(
(this.useCommunicationForFileUpload()
? this.fileUploaderService.uploadMarkdownFileInCurrentMetisConversation(file, this.metisService.getCourse().id, this.metisService.getCurrentConversation()?.id)
: this.fileUploaderService.uploadMarkdownFile(file)
).then(
(response) => {
const extension = file.name.split('.').last()?.toLocaleLowerCase();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<span class="toggle-content" jhiTranslate="{{ showContent ? 'artemisApp.metis.post.collapseContent' : 'artemisApp.metis.post.showContent' }}"></span>
@if (showContent) {
<div>
<!-- TODO: fix track expression -->
<!-- TODO: check track expression -->
@for (postingContentPart of postingContentParts(); track $index) {
<jhi-posting-content-part
[postingContentPart]="postingContentPart"
Expand All @@ -26,7 +26,7 @@
<!-- not in preview mode: content always shown -->
@if (!previewMode) {
<div class="pb-1">
<!-- TODO: fix track expression... -->
<!-- TODO: check track expression -->
@for (postingContentPart of postingContentParts(); track $index) {
<jhi-posting-content-part
[postingContentPart]="postingContentPart"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
[defaultActions]="defaultActions"
[lectureReferenceAction]="lectureAttachmentReferenceAction"
[enableFileUpload]="true"
[useCommunicationForFileUpload]="true"
[colorAction]="undefined"
[enableResize]="false"
[showDefaultPreview]="false"
Expand Down

0 comments on commit a17ee25

Please sign in to comment.