From 1138b9420204b105b9c2b49dc763aa6ba349b375 Mon Sep 17 00:00:00 2001 From: Tim Cremer Date: Tue, 26 Nov 2024 18:24:54 +0100 Subject: [PATCH 1/6] Allow tutors to monitor channels as moderator for course wide channels. --- .../service/conversation/ConversationService.java | 4 +++- .../answer-post-reactions-bar.component.ts | 7 ++++--- .../post-reactions-bar/post-reactions-bar.component.ts | 10 ++++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/communication/service/conversation/ConversationService.java b/src/main/java/de/tum/cit/aet/artemis/communication/service/conversation/ConversationService.java index c2a1761b6b46..1a981ee84f99 100644 --- a/src/main/java/de/tum/cit/aet/artemis/communication/service/conversation/ConversationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/communication/service/conversation/ConversationService.java @@ -143,7 +143,9 @@ public Optional isMemberOrCreateForCourseWideElseThrow(Long conver if (conversation instanceof Channel channel && channel.getIsCourseWide()) { ConversationParticipant conversationParticipant = ConversationParticipant.createWithDefaultValues(user, channel); - conversationParticipant.setIsModerator(authorizationCheckService.isAtLeastInstructorInCourse(channel.getCourse(), user)); + boolean canBecomeModerator = (channel.getIsAnnouncementChannel() ? authorizationCheckService.isAtLeastInstructorInCourse(channel.getCourse(), user) + : authorizationCheckService.isAtLeastTeachingAssistantInCourse(channel.getCourse(), user)); + conversationParticipant.setIsModerator(canBecomeModerator); lastReadDate.ifPresent(conversationParticipant::setLastRead); conversationParticipantRepository.saveAndFlush(conversationParticipant); } diff --git a/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts b/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts index 9023089670d5..77244405a0c8 100644 --- a/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts +++ b/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts @@ -69,10 +69,11 @@ export class AnswerPostReactionsBarComponent extends PostingsReactionsBarDirecti this.isAuthorOfOriginalPost = this.metisService.metisUserIsAuthorOfPosting(this.posting.post!); this.isAnswerOfAnnouncement = getAsChannelDTO(this.posting.post?.conversation)?.isAnnouncementChannel ?? false; const isCourseWideChannel = getAsChannelDTO(this.posting.post?.conversation)?.isCourseWide ?? false; - const isAtLeastInstructorInCourse = this.metisService.metisUserIsAtLeastInstructorInCourse(); + const isAtLeastTutorInCourse = this.metisService.metisUserIsAtLeastTutorInCourse(); + const canDeleteAndEditAnnouncement = this.isAnswerOfAnnouncement ? this.metisService.metisUserIsAtLeastInstructorInCourse() : true; const mayEditOrDeleteOtherUsersAnswer = - (isCourseWideChannel && isAtLeastInstructorInCourse) || (getAsChannelDTO(this.metisService.getCurrentConversation())?.hasChannelModerationRights ?? false); - this.mayEditOrDelete = !this.isReadOnlyMode && (this.isAuthorOfPosting || mayEditOrDeleteOtherUsersAnswer); + (isCourseWideChannel && isAtLeastTutorInCourse) || (getAsChannelDTO(this.metisService.getCurrentConversation())?.hasChannelModerationRights ?? false); + this.mayEditOrDelete = !this.isReadOnlyMode && (this.isAuthorOfPosting || mayEditOrDeleteOtherUsersAnswer) && canDeleteAndEditAnnouncement; this.mayEditOrDeleteOutput.emit(this.mayEditOrDelete); } diff --git a/src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts b/src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts index 1d039326b20b..81c130260331 100644 --- a/src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts +++ b/src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts @@ -86,7 +86,7 @@ export class PostReactionsBarComponent extends PostingsReactionsBarDirective Date: Tue, 26 Nov 2024 18:42:40 +0100 Subject: [PATCH 2/6] merge conflict --- .../answer-post-reactions-bar.component.ts | 7 ++++--- .../post-reactions-bar/post-reactions-bar.component.ts | 7 +++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts b/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts index c287db1b1d7b..1770846a58d6 100644 --- a/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts +++ b/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts @@ -73,10 +73,11 @@ export class AnswerPostReactionsBarComponent extends PostingsReactionsBarDirecti this.isAuthorOfOriginalPost = this.metisService.metisUserIsAuthorOfPosting(this.posting.post!); this.isAnswerOfAnnouncement = getAsChannelDTO(this.posting.post?.conversation)?.isAnnouncementChannel ?? false; const isCourseWideChannel = getAsChannelDTO(this.posting.post?.conversation)?.isCourseWide ?? false; - const isAtLeastInstructorInCourse = this.metisService.metisUserIsAtLeastInstructorInCourse(); + const isAtLeastTutorInCourse = this.metisService.metisUserIsAtLeastTutorInCourse(); + const canDeleteAnnouncement = this.isAnswerOfAnnouncement ? this.metisService.metisUserIsAtLeastInstructorInCourse() : true; const mayEditOrDeleteOtherUsersAnswer = - (isCourseWideChannel && isAtLeastInstructorInCourse) || (getAsChannelDTO(this.metisService.getCurrentConversation())?.hasChannelModerationRights ?? false); - this.mayDelete = !this.isReadOnlyMode && (this.isAuthorOfPosting || mayEditOrDeleteOtherUsersAnswer); + (isCourseWideChannel && isAtLeastTutorInCourse) || (getAsChannelDTO(this.metisService.getCurrentConversation())?.hasChannelModerationRights ?? false); + this.mayDelete = !this.isReadOnlyMode && (this.isAuthorOfPosting || mayEditOrDeleteOtherUsersAnswer) && canDeleteAnnouncement; this.mayDeleteOutput.emit(this.mayDelete); } diff --git a/src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts b/src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts index 569eec43b75f..22aeac455b8d 100644 --- a/src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts +++ b/src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts @@ -203,9 +203,12 @@ export class PostReactionsBarComponent extends PostingsReactionsBarDirective Date: Tue, 26 Nov 2024 20:53:31 +0100 Subject: [PATCH 3/6] tutors should be able to pin the messages --- .../post-reactions-bar/post-reactions-bar.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts b/src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts index 22aeac455b8d..9213fcaeae56 100644 --- a/src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts +++ b/src/main/webapp/app/shared/metis/posting-reactions-bar/post-reactions-bar/post-reactions-bar.component.ts @@ -100,7 +100,7 @@ export class PostReactionsBarComponent extends PostingsReactionsBarDirective Date: Wed, 27 Nov 2024 08:56:42 +0100 Subject: [PATCH 4/6] fix tests --- .../answer-post-reactions-bar.component.spec.ts | 5 +++-- .../post-reactions-bar.component.spec.ts | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/test/javascript/spec/component/shared/metis/postings-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.spec.ts b/src/test/javascript/spec/component/shared/metis/postings-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.spec.ts index 583d160dda7a..96517283b95c 100644 --- a/src/test/javascript/spec/component/shared/metis/postings-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.spec.ts +++ b/src/test/javascript/spec/component/shared/metis/postings-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.spec.ts @@ -131,6 +131,7 @@ describe('AnswerPostReactionsBarComponent', () => { it('should display the delete option to instructor if posting is in course-wide channel from a student', () => { metisServiceUserIsAtLeastInstructorMock.mockReturnValue(true); metisServiceUserPostingAuthorMock.mockReturnValue(false); + metisServiceUserIsAtLeastTutorMock.mockReturnValue(true); component.posting = { ...metisResolvingAnswerPostUser1, post: { ...metisPostInChannel } }; component.posting.authorRole = UserRole.USER; component.ngOnInit(); @@ -158,7 +159,7 @@ describe('AnswerPostReactionsBarComponent', () => { expect(getEditButton()).not.toBeNull(); }); - it('should not display edit and delete options to tutor if posting is in course-wide channel from a student', () => { + it('should display edit and delete options to tutor if posting is in course-wide channel from a student', () => { metisServiceUserIsAtLeastInstructorMock.mockReturnValue(false); metisServiceUserIsAtLeastTutorMock.mockReturnValue(true); metisServiceUserPostingAuthorMock.mockReturnValue(false); @@ -167,7 +168,7 @@ describe('AnswerPostReactionsBarComponent', () => { component.ngOnInit(); fixture.detectChanges(); expect(getEditButton()).toBeNull(); - expect(getDeleteButton()).toBeNull(); + expect(getDeleteButton()).not.toBeNull(); }); it('should not display edit and delete options to users that are neither author or tutor', () => { diff --git a/src/test/javascript/spec/component/shared/metis/postings-reactions-bar/post-reactions-bar/post-reactions-bar.component.spec.ts b/src/test/javascript/spec/component/shared/metis/postings-reactions-bar/post-reactions-bar/post-reactions-bar.component.spec.ts index ce01de09f53b..c567c465fe56 100644 --- a/src/test/javascript/spec/component/shared/metis/postings-reactions-bar/post-reactions-bar/post-reactions-bar.component.spec.ts +++ b/src/test/javascript/spec/component/shared/metis/postings-reactions-bar/post-reactions-bar/post-reactions-bar.component.spec.ts @@ -223,7 +223,7 @@ describe('PostReactionsBarComponent', () => { expect(debugElement.query(By.directive(ConfirmIconComponent))).toBeNull(); }); - it('should not display edit and delete options to tutor if posting is in course-wide channel', () => { + it('should not display edit option but should display delete option to tutor if posting is in course-wide channel', () => { metisServiceUserIsAtLeastInstructorStub.mockReturnValue(false); metisServiceUserIsAtLeastTutorStub.mockReturnValue(true); metisServiceUserIsAuthorOfPostingStub.mockReturnValue(false); @@ -232,7 +232,7 @@ describe('PostReactionsBarComponent', () => { component.ngOnInit(); fixture.detectChanges(); expect(getEditButton()).toBeNull(); - expect(getDeleteButton()).toBeNull(); + expect(getDeleteButton()).not.toBeNull(); }); it('should not display edit and delete options to tutor if posting is announcement', () => { @@ -257,11 +257,14 @@ describe('PostReactionsBarComponent', () => { it('should display the delete option to instructor if posting is in course-wide channel from a student', () => { metisServiceUserIsAtLeastInstructorStub.mockReturnValue(true); + metisServiceUserIsAtLeastTutorStub.mockReturnValue(true); metisServiceUserIsAuthorOfPostingStub.mockReturnValue(false); component.posting = { ...metisPostInChannel }; component.posting.authorRole = UserRole.USER; + component.ngOnInit(); fixture.detectChanges(); + component.setMayDelete(); expect(getDeleteButton()).not.toBeNull(); }); From 292cb2e03325144692a4e764a4ef99d2c0f6896e Mon Sep 17 00:00:00 2001 From: Tim Cremer Date: Fri, 29 Nov 2024 10:07:14 +0100 Subject: [PATCH 5/6] Improve Code --- .../answer-post-reactions-bar.component.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts b/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts index 111c2921f082..8d756b5930e7 100644 --- a/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts +++ b/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts @@ -74,11 +74,10 @@ export class AnswerPostReactionsBarComponent extends PostingsReactionsBarDirecti this.isAuthorOfOriginalPost = this.metisService.metisUserIsAuthorOfPosting(this.posting.post!); this.isAnswerOfAnnouncement = getAsChannelDTO(this.posting.post?.conversation)?.isAnnouncementChannel ?? false; const isCourseWideChannel = getAsChannelDTO(this.posting.post?.conversation)?.isCourseWide ?? false; - const isAtLeastTutorInCourse = this.metisService.metisUserIsAtLeastTutorInCourse(); - const canDeleteAnnouncement = this.isAnswerOfAnnouncement ? this.metisService.metisUserIsAtLeastInstructorInCourse() : true; - const mayEditOrDeleteOtherUsersAnswer = - (isCourseWideChannel && isAtLeastTutorInCourse) || (getAsChannelDTO(this.metisService.getCurrentConversation())?.hasChannelModerationRights ?? false); - this.mayDelete = !this.isReadOnlyMode && (this.isAuthorOfPosting || mayEditOrDeleteOtherUsersAnswer) && canDeleteAnnouncement; + const canDeletePost = this.isAnswerOfAnnouncement ? this.metisService.metisUserIsAtLeastInstructorInCourse() : this.metisService.metisUserIsAtLeastTutorInCourse(); + const mayDeleteOtherUsersAnswer = + (isCourseWideChannel && canDeletePost) || (getAsChannelDTO(this.metisService.getCurrentConversation())?.hasChannelModerationRights ?? false); + this.mayDelete = !this.isReadOnlyMode && (this.isAuthorOfPosting || mayDeleteOtherUsersAnswer) && canDeletePost; this.mayDeleteOutput.emit(this.mayDelete); } From 788d9390e5a5824ec77392ed7c7f19f8ffafb9b7 Mon Sep 17 00:00:00 2001 From: Tim Cremer Date: Mon, 2 Dec 2024 12:56:29 +0100 Subject: [PATCH 6/6] Fixed failing --- .../answer-post-reactions-bar.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts b/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts index 8d756b5930e7..8a843639365a 100644 --- a/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts +++ b/src/main/webapp/app/shared/metis/posting-reactions-bar/answer-post-reactions-bar/answer-post-reactions-bar.component.ts @@ -77,7 +77,7 @@ export class AnswerPostReactionsBarComponent extends PostingsReactionsBarDirecti const canDeletePost = this.isAnswerOfAnnouncement ? this.metisService.metisUserIsAtLeastInstructorInCourse() : this.metisService.metisUserIsAtLeastTutorInCourse(); const mayDeleteOtherUsersAnswer = (isCourseWideChannel && canDeletePost) || (getAsChannelDTO(this.metisService.getCurrentConversation())?.hasChannelModerationRights ?? false); - this.mayDelete = !this.isReadOnlyMode && (this.isAuthorOfPosting || mayDeleteOtherUsersAnswer) && canDeletePost; + this.mayDelete = !this.isReadOnlyMode && (this.isAuthorOfPosting || (mayDeleteOtherUsersAnswer && canDeletePost)); this.mayDeleteOutput.emit(this.mayDelete); }