Skip to content

Commit

Permalink
Fix attachment 404 issue when referencing in post
Browse files Browse the repository at this point in the history
  • Loading branch information
PaRangger committed Jan 17, 2025
1 parent d4d178b commit 623d861
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 37 deletions.
13 changes: 9 additions & 4 deletions src/main/webapp/app/shared/http/file.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,19 @@ export class FileService {
* @param downloadName the name given to the attachment
*/
downloadFileByAttachmentName(downloadUrl: string, downloadName: string) {
const normalizedDownloadUrl = this.createAttachmentFileUrl(downloadUrl, downloadName, true);
const newWindow = window.open('about:blank');
newWindow!.location.href = normalizedDownloadUrl;
return newWindow;
}

createAttachmentFileUrl(downloadUrl: string, downloadName: string, encodeName: boolean) {
const downloadUrlComponents = downloadUrl.split('/');
// take the last element
const extension = downloadUrlComponents.pop()!.split('.').pop();
const restOfUrl = downloadUrlComponents.join('/');
const normalizedDownloadUrl = restOfUrl + '/' + encodeURIComponent(downloadName + '.' + extension);
const newWindow = window.open('about:blank');
newWindow!.location.href = normalizedDownloadUrl;
return newWindow;
const encodedDownloadName = encodeName ? encodeURIComponent(downloadName + '.' + extension) : downloadName + '.' + extension;
return restOfUrl + '/' + encodedDownloadName;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { OrderedListAction } from 'app/shared/monaco-editor/model/actions/ordere
import { StrikethroughAction } from 'app/shared/monaco-editor/model/actions/strikethrough.action';
import { PostingContentComponent } from '../posting-content/posting-content.components';
import { NgStyle } from '@angular/common';
import { FileService } from 'app/shared/http/file.service';

@Component({
selector: 'jhi-posting-markdown-editor',
Expand All @@ -64,6 +65,7 @@ import { NgStyle } from '@angular/common';
export class PostingMarkdownEditorComponent implements OnInit, ControlValueAccessor, AfterContentChecked, AfterViewInit {
private cdref = inject(ChangeDetectorRef);
private metisService = inject(MetisService);
private fileService = inject(FileService);
private courseManagementService = inject(CourseManagementService);
private lectureService = inject(LectureService);
private channelService = inject(ChannelService);
Expand Down Expand Up @@ -119,7 +121,7 @@ export class PostingMarkdownEditorComponent implements OnInit, ControlValueAcces
...faqAction,
];

this.lectureAttachmentReferenceAction = new LectureAttachmentReferenceAction(this.metisService, this.lectureService);
this.lectureAttachmentReferenceAction = new LectureAttachmentReferenceAction(this.metisService, this.lectureService, this.fileService);
}

ngAfterViewInit(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { Slide } from 'app/entities/lecture-unit/slide.model';
import { LectureUnitType } from 'app/entities/lecture-unit/lectureUnit.model';
import { TextEditor } from 'app/shared/monaco-editor/model/actions/adapter/text-editor.interface';
import { sanitizeStringForMarkdownEditor } from 'app/shared/util/markdown.util';
import { FileService } from 'app/shared/http/file.service';
import { cloneDeep } from 'lodash-es';

interface LectureWithDetails {
id: number;
Expand Down Expand Up @@ -37,19 +39,36 @@ export class LectureAttachmentReferenceAction extends TextEditorAction {
constructor(
private readonly metisService: MetisService,
private readonly lectureService: LectureService,
private readonly fileService: FileService,
) {
super(LectureAttachmentReferenceAction.ID, 'artemisApp.metis.editor.lecture');
firstValueFrom(this.lectureService.findAllByCourseIdWithSlides(this.metisService.getCourse().id!)).then((response) => {
const lectures = response.body;
if (lectures) {
this.lecturesWithDetails = lectures
.filter((lecture) => !!lecture.id && !!lecture.title)
.map((lecture) => ({
id: lecture.id!,
title: lecture.title!,
attachmentUnits: lecture.lectureUnits?.filter((unit) => unit.type === LectureUnitType.ATTACHMENT),
attachments: lecture.attachments,
}));
.map((lecture) => {
let attachmentCopy = cloneDeep(lecture.attachments);

if (attachmentCopy) {
attachmentCopy = attachmentCopy.map((attachment) => {
if (attachment.link && attachment.name) {
attachment.link = this.fileService.createAttachmentFileUrl(attachment.link!, attachment.name!, false);
}

return attachment;
});
} else {
attachmentCopy = lecture.attachments;
}

return {
id: lecture.id!,
title: lecture.title!,
attachmentUnits: lecture.lectureUnits?.filter((unit) => unit.type === LectureUnitType.ATTACHMENT),
attachments: attachmentCopy,
};
});
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,18 @@ import { TextEditorRange } from 'app/shared/monaco-editor/model/actions/adapter/
import { TextEditorPosition } from 'app/shared/monaco-editor/model/actions/adapter/text-editor-position.model';
import { BulletedListAction } from 'app/shared/monaco-editor/model/actions/bulleted-list.action';
import { OrderedListAction } from 'app/shared/monaco-editor/model/actions/ordered-list.action';
import { ListAction } from '../../../../../../../main/webapp/app/shared/monaco-editor/model/actions/list.action';
import { ListAction } from 'app/shared/monaco-editor/model/actions/list.action';
import monaco from 'monaco-editor';
import { FileService } from 'app/shared/http/file.service';
import { MockFileService } from '../../../../helpers/mocks/service/mock-file.service';

describe('PostingsMarkdownEditor', () => {
let component: PostingMarkdownEditorComponent;
let fixture: ComponentFixture<PostingMarkdownEditorComponent>;
let debugElement: DebugElement;
let mockMarkdownEditorComponent: MarkdownEditorMonacoComponent;
let metisService: MetisService;
let fileService: FileService;
let lectureService: LectureService;
let findLectureWithDetailsSpy: jest.SpyInstance;

Expand Down Expand Up @@ -120,6 +123,7 @@ describe('PostingsMarkdownEditor', () => {
return TestBed.configureTestingModule({
providers: [
{ provide: MetisService, useClass: MockMetisService },
{ provide: FileService, useClass: MockFileService },
MockProvider(LectureService),
MockProvider(CourseManagementService),
MockProvider(ChannelService),
Expand All @@ -134,6 +138,7 @@ describe('PostingsMarkdownEditor', () => {
fixture = TestBed.createComponent(PostingMarkdownEditorComponent);
component = fixture.componentInstance;
debugElement = fixture.debugElement;
fileService = TestBed.inject(FileService);
metisService = TestBed.inject(MetisService);
lectureService = TestBed.inject(LectureService);

Expand All @@ -154,14 +159,14 @@ describe('PostingsMarkdownEditor', () => {
containDefaultActions(component.defaultActions);
expect(component.defaultActions).toEqual(expect.arrayContaining([expect.any(UserMentionAction), expect.any(ChannelReferenceAction)]));

expect(component.lectureAttachmentReferenceAction).toEqual(new LectureAttachmentReferenceAction(metisService, lectureService));
expect(component.lectureAttachmentReferenceAction).toEqual(new LectureAttachmentReferenceAction(metisService, lectureService, fileService));
});

it('should have set the correct default commands on init if communication is disabled', () => {
jest.spyOn(CourseModel, 'isCommunicationEnabled').mockReturnValueOnce(false);
component.ngOnInit();
containDefaultActions(component.defaultActions);
expect(component.lectureAttachmentReferenceAction).toEqual(new LectureAttachmentReferenceAction(metisService, lectureService));
expect(component.lectureAttachmentReferenceAction).toEqual(new LectureAttachmentReferenceAction(metisService, lectureService, fileService));
});

function containDefaultActions(defaultActions: TextEditorAction[]) {
Expand All @@ -186,15 +191,15 @@ describe('PostingsMarkdownEditor', () => {
component.ngOnInit();
containDefaultActions(component.defaultActions);
expect(component.defaultActions).toEqual(expect.arrayContaining([expect.any(FaqReferenceAction)]));
expect(component.lectureAttachmentReferenceAction).toEqual(new LectureAttachmentReferenceAction(metisService, lectureService));
expect(component.lectureAttachmentReferenceAction).toEqual(new LectureAttachmentReferenceAction(metisService, lectureService, fileService));
});

it('should have set the correct default commands on init if faq is disabled', () => {
jest.spyOn(CourseModel, 'isFaqEnabled').mockReturnValueOnce(false);
component.ngOnInit();
containDefaultActions(component.defaultActions);
expect(component.defaultActions).toEqual(expect.not.arrayContaining([expect.any(FaqReferenceAction)]));
expect(component.lectureAttachmentReferenceAction).toEqual(new LectureAttachmentReferenceAction(metisService, lectureService));
expect(component.lectureAttachmentReferenceAction).toEqual(new LectureAttachmentReferenceAction(metisService, lectureService, fileService));
});

it('should show the correct amount of characters below the markdown input', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@ import { Attachment } from 'app/entities/attachment.model';
import dayjs from 'dayjs/esm';
import { FaqReferenceAction } from 'app/shared/monaco-editor/model/actions/communication/faq-reference.action';
import { Faq } from 'app/entities/faq.model';
import { FileService } from 'app/shared/http/file.service';
import { MockFileService } from '../../../helpers/mocks/service/mock-file.service';

describe('MonacoEditorCommunicationActionIntegration', () => {
let comp: MonacoEditorComponent;
let fixture: ComponentFixture<MonacoEditorComponent>;
let metisService: MetisService;
let fileService: FileService;
let courseManagementService: CourseManagementService;
let channelService: ChannelService;
let lectureService: LectureService;
Expand All @@ -46,34 +49,34 @@ describe('MonacoEditorCommunicationActionIntegration', () => {
let exerciseReferenceAction: ExerciseReferenceAction;
let faqReferenceAction: FaqReferenceAction;

beforeEach(() => {
return TestBed.configureTestingModule({
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MonacoEditorComponent],
providers: [
{ provide: MetisService, useClass: MockMetisService },
{ provide: FileService, useClass: MockFileService },
{ provide: TranslateService, useClass: MockTranslateService },
{ provide: LocalStorageService, useClass: MockLocalStorageService },
MockProvider(LectureService),
MockProvider(CourseManagementService),
MockProvider(ChannelService),
],
})
.compileComponents()
.then(() => {
global.ResizeObserver = jest.fn().mockImplementation((callback: ResizeObserverCallback) => {
return new MockResizeObserver(callback);
});
fixture = TestBed.createComponent(MonacoEditorComponent);
comp = fixture.componentInstance;
metisService = TestBed.inject(MetisService);
courseManagementService = TestBed.inject(CourseManagementService);
lectureService = TestBed.inject(LectureService);
channelService = TestBed.inject(ChannelService);
channelReferenceAction = new ChannelReferenceAction(metisService, channelService);
userMentionAction = new UserMentionAction(courseManagementService, metisService);
exerciseReferenceAction = new ExerciseReferenceAction(metisService);
faqReferenceAction = new FaqReferenceAction(metisService);
});
}).compileComponents();

global.ResizeObserver = jest.fn().mockImplementation((callback: ResizeObserverCallback) => {
return new MockResizeObserver(callback);
});
fixture = TestBed.createComponent(MonacoEditorComponent);
comp = fixture.componentInstance;
metisService = TestBed.inject(MetisService);
fileService = TestBed.inject(FileService);
courseManagementService = TestBed.inject(CourseManagementService);
lectureService = TestBed.inject(LectureService);
channelService = TestBed.inject(ChannelService);
channelReferenceAction = new ChannelReferenceAction(metisService, channelService);
userMentionAction = new UserMentionAction(courseManagementService, metisService);
exerciseReferenceAction = new ExerciseReferenceAction(metisService);
faqReferenceAction = new FaqReferenceAction(metisService);
});

afterEach(() => {
Expand Down Expand Up @@ -280,7 +283,7 @@ describe('MonacoEditorCommunicationActionIntegration', () => {
beforeEach(() => {
lectures = metisService.getCourse().lectures!;
jest.spyOn(lectureService, 'findAllByCourseIdWithSlides').mockReturnValue(of(new HttpResponse({ body: lectures, status: 200 })));
lectureAttachmentReferenceAction = new LectureAttachmentReferenceAction(metisService, lectureService);
lectureAttachmentReferenceAction = new LectureAttachmentReferenceAction(metisService, lectureService, fileService);
});

afterEach(() => {
Expand All @@ -295,7 +298,10 @@ describe('MonacoEditorCommunicationActionIntegration', () => {
id: lecture.id!,
title: lecture.title!,
attachmentUnits: lecture.lectureUnits?.filter((unit) => unit.type === LectureUnitType.ATTACHMENT),
attachments: lecture.attachments,
attachments: lecture.attachments?.map((attachment) => ({
...attachment,
link: attachment.link && attachment.name ? fileService.createAttachmentFileUrl(attachment.link, attachment.name, false) : attachment.link,
})),
}));

expect(lectureAttachmentReferenceAction.lecturesWithDetails).toEqual(lecturesWithDetails);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export class MockFileService {
return of();
};

createAttachmentFileUrl(downloadUrl: string, downloadName: string, encodeName: boolean) {
return 'attachments/' + downloadName.replace(' ', '-') + '.pdf';
}

replaceLectureAttachmentPrefixAndUnderscores = (link: string) => link;
replaceAttachmentPrefixAndUnderscores = (link: string) => link;
}

0 comments on commit 623d861

Please sign in to comment.