From 61b03c2c8d10f2038f80cf42ddd9bda76fd1a61b Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 30 Aug 2023 19:46:55 +0200 Subject: [PATCH 01/76] Add courseInformationSharingMessagingCodeOfConduct --- .../de/tum/in/www1/artemis/domain/Course.java | 10 ++++++++++ .../changelog/20230830183901_changelog.xml | 10 ++++++++++ .../resources/config/liquibase/master.xml | 1 + .../manage/course-update.component.html | 20 +++++++++++++++++++ .../course/manage/course-update.component.ts | 1 + src/main/webapp/app/entities/course.model.ts | 1 + src/main/webapp/i18n/de/course.json | 6 +++++- src/main/webapp/i18n/en/course.json | 6 +++++- 8 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/main/resources/config/liquibase/changelog/20230830183901_changelog.xml diff --git a/src/main/java/de/tum/in/www1/artemis/domain/Course.java b/src/main/java/de/tum/in/www1/artemis/domain/Course.java index f549df2d6360..157e7940d339 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/Course.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/Course.java @@ -130,6 +130,9 @@ public class Course extends DomainObject { @JsonView(QuizView.Before.class) private CourseInformationSharingConfiguration courseInformationSharingConfiguration = CourseInformationSharingConfiguration.COMMUNICATION_AND_MESSAGING; // default value + @Column(name = "info_sharing_messaging_code_of_conduct") + private String courseInformationSharingMessagingCodeOfConduct; + @Column(name = "max_complaints", nullable = false) @JsonView(QuizView.Before.class) private Integer maxComplaints = 3; // default value @@ -981,4 +984,11 @@ public void setCourseInformationSharingConfiguration(CourseInformationSharingCon this.courseInformationSharingConfiguration = courseInformationSharingConfiguration; } + public String getCourseInformationSharingMessagingCodeOfConduct() { + return this.courseInformationSharingMessagingCodeOfConduct; + } + + public void setCourseInformationSharingMessagingCodeOfConduct(String courseInformationSharingMessagingCodeOfConduct) { + this.courseInformationSharingMessagingCodeOfConduct = courseInformationSharingMessagingCodeOfConduct; + } } diff --git a/src/main/resources/config/liquibase/changelog/20230830183901_changelog.xml b/src/main/resources/config/liquibase/changelog/20230830183901_changelog.xml new file mode 100644 index 000000000000..8f326ee7627e --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/20230830183901_changelog.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml index 119bfabcf15c..2120c3a22010 100644 --- a/src/main/resources/config/liquibase/master.xml +++ b/src/main/resources/config/liquibase/master.xml @@ -49,6 +49,7 @@ + diff --git a/src/main/webapp/app/course/manage/course-update.component.html b/src/main/webapp/app/course/manage/course-update.component.html index 82da121296e2..9fed2d69e435 100644 --- a/src/main/webapp/app/course/manage/course-update.component.html +++ b/src/main/webapp/app/course/manage/course-update.component.html @@ -336,6 +336,26 @@
ngbTooltip="{{ 'artemisApp.course.courseCommunicationSetting.messagingEnabled.tooltip' | artemisTranslate }}" > +
+ + + +
Date: Thu, 31 Aug 2023 17:48:19 +0200 Subject: [PATCH 02/76] Add top bar button --- .../course-conversations.component.html | 4 ++++ .../course-conversations.component.ts | 22 ++++++++++++++++++- src/main/webapp/i18n/de/conversation.json | 7 ++++++ src/main/webapp/i18n/en/conversation.json | 7 ++++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.html b/src/main/webapp/app/overview/course-conversations/course-conversations.component.html index 06764d4c21ec..8bd5fb590eb1 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.html +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.html @@ -1,3 +1,7 @@ + + + +
diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts index b4ae773043ae..e738ec855f58 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; import { ConversationDto } from 'app/entities/metis/conversation/conversation.model'; import { Post } from 'app/entities/metis/post.model'; import { ActivatedRoute, Router } from '@angular/router'; @@ -8,6 +8,7 @@ import { getAsChannelDto } from 'app/entities/metis/conversation/channel.model'; import { MetisService } from 'app/shared/metis/metis.service'; import { Course } from 'app/entities/course.model'; import { PageType } from 'app/shared/metis/metis.util'; +import { BarControlConfiguration } from 'app/shared/tab-bar/tab-bar'; @Component({ selector: 'jhi-course-conversations', @@ -24,6 +25,14 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { postInThread?: Post; activeConversation?: ConversationDto = undefined; conversationsOfUser: ConversationDto[] = []; + + // The extracted controls template from our template to be rendered in the top bar of "CourseOverviewComponent" + @ViewChild('controls', { static: false }) private controls: TemplateRef; + // Provides the control configuration to be read and used by "CourseOverviewComponent" + public readonly controlConfiguration: BarControlConfiguration = { + subject: new Subject>(), + }; + // MetisConversationService is created in course overview, so we can use it here constructor( private router: Router, @@ -83,11 +92,22 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { }); } + ngAfterViewInit(): void { + // Send our controls template to parent so it will be rendered in the top bar + if (this.controls) { + this.controlConfiguration.subject!.next(this.controls); + } + } + ngOnDestroy() { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); } + openDialog() { + console.log('Open Dialog'); + } + private subscribeToActiveConversation() { this.metisConversationService.activeConversation$.pipe(takeUntil(this.ngUnsubscribe)).subscribe((conversation: ConversationDto) => { this.activeConversation = conversation; diff --git a/src/main/webapp/i18n/de/conversation.json b/src/main/webapp/i18n/de/conversation.json index 57a010abfd4a..f2f6a8c4b430 100644 --- a/src/main/webapp/i18n/de/conversation.json +++ b/src/main/webapp/i18n/de/conversation.json @@ -8,6 +8,13 @@ "mainParagraph": "Gib im Chat keine vertraulichen Informationen wie Passwörter, Kreditkartennummern oder persönliche Identifikationsnummern weiter. Diese Informationen sind sensibel und könnten für Identitätsdiebstahl oder Betrug verwendet werden, wenn sie in die falschen Hände geraten.", "lastParagraph": "Bewahre deine persönlichen Daten sicher auf, indem du sie für dich behältst und sie nicht online weitergibst." }, + "conversation": { + "codeOfConduct": { + "topBar": { + "button": "Verhaltens-Kodex" + } + } + }, "conversationsLayout": { "tabTitle": "Kursnachrichten", "breadCrumbLabel": "Nachrichten", diff --git a/src/main/webapp/i18n/en/conversation.json b/src/main/webapp/i18n/en/conversation.json index dc8a6f15fb6b..ddc612abcd3b 100644 --- a/src/main/webapp/i18n/en/conversation.json +++ b/src/main/webapp/i18n/en/conversation.json @@ -8,6 +8,13 @@ "mainParagraph": "Do not share confidential information like passwords, credit card numbers, or personal identification numbers in the chat. This information is sensitive and could potentially be used for identity theft or fraud if it falls into the wrong hands.", "lastParagraph": "Keep your personal information safe by keeping it to yourself and not sharing it online." }, + "conversation": { + "codeOfConduct": { + "topBar": { + "button": "Code of Conduct" + } + } + }, "conversationsLayout": { "tabTitle": "Course Messages", "breadCrumbLabel": "Messages", From 8e45899d013917c3b821f56cd3f59725088c69c1 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 31 Aug 2023 23:15:13 +0200 Subject: [PATCH 03/76] Show code of conduct in dialog --- .../course-conversations.component.ts | 6 +++++- .../course-conversations.module.ts | 4 ++++ .../conversation-code-of-conduct-dialog.component.ts | 10 ++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.ts diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts index e738ec855f58..21ff3195384b 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts @@ -9,6 +9,8 @@ import { MetisService } from 'app/shared/metis/metis.service'; import { Course } from 'app/entities/course.model'; import { PageType } from 'app/shared/metis/metis.util'; import { BarControlConfiguration } from 'app/shared/tab-bar/tab-bar'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { ConversationCodeOfConductDialogComponent } from 'app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component'; @Component({ selector: 'jhi-course-conversations', @@ -39,6 +41,7 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { private activatedRoute: ActivatedRoute, public metisConversationService: MetisConversationService, public metisService: MetisService, + private modalService: NgbModal, ) {} getAsChannel = getAsChannelDto; @@ -105,7 +108,8 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { } openDialog() { - console.log('Open Dialog'); + const modalRef: NgbModalRef = this.modalService.open(ConversationCodeOfConductDialogComponent, {}); + modalRef.componentInstance.codeOfConduct = this.course?.courseInformationSharingMessagingCodeOfConduct; } private subscribeToActiveConversation() { diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts index 5aac80556033..eb1d713fa9c0 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts @@ -20,6 +20,7 @@ import { ConversationAddUsersDialogComponent } from './dialogs/conversation-add- import { ConversationAddUsersFormComponent } from './dialogs/conversation-add-users-dialog/add-users-form/conversation-add-users-form.component'; import { CourseUsersSelectorModule } from 'app/shared/course-users-selector/course-users-selector.module'; import { ConversationMembersComponent } from './dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-members.component'; +import { ConversationCodeOfConductDialogComponent } from 'app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component'; import { ConversationDetailDialogComponent } from './dialogs/conversation-detail-dialog/conversation-detail-dialog.component'; import { ConversationInfoComponent } from './dialogs/conversation-detail-dialog/tabs/conversation-info/conversation-info.component'; import { ConversationMemberRowComponent } from './dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component'; @@ -30,6 +31,7 @@ import { ConversationSidebarEntryComponent } from './layout/conversation-selecti import { OneToOneChatCreateDialogComponent } from './dialogs/one-to-one-chat-create-dialog/one-to-one-chat-create-dialog.component'; import { GroupChatCreateDialogComponent } from './dialogs/group-chat-create-dialog/group-chat-create-dialog.component'; import { GroupChatIconComponent } from './other/group-chat-icon/group-chat-icon.component'; +import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; const routes: Routes = [ { @@ -51,6 +53,7 @@ const routes: Routes = [ ArtemisDataTableModule, InfiniteScrollModule, CourseUsersSelectorModule, + ArtemisMarkdownModule, ], declarations: [ CourseConversationsComponent, @@ -67,6 +70,7 @@ const routes: Routes = [ ConversationAddUsersDialogComponent, ConversationAddUsersFormComponent, ConversationMembersComponent, + ConversationCodeOfConductDialogComponent, ConversationDetailDialogComponent, ConversationInfoComponent, ConversationMemberRowComponent, diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.ts new file mode 100644 index 000000000000..de863d20f6d0 --- /dev/null +++ b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.ts @@ -0,0 +1,10 @@ +import { Component, Input } from '@angular/core'; +import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component'; + +@Component({ + selector: 'jhi-conversation-code-of-conduct-dialog', + template: ` `, +}) +export class ConversationCodeOfConductDialogComponent extends AbstractDialogComponent { + @Input() codeOfConduct: string; +} From b92a7f8c6d976064e4add3022bcad33f148120b3 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Tue, 5 Sep 2023 13:43:07 +0200 Subject: [PATCH 04/76] Add accept dialog --- .../course-conversations.component.ts | 12 ++++++++++-- .../course-conversations.module.ts | 2 ++ ...n-accept-code-of-conduct-dialog.component.html | 6 ++++++ ...n-accept-code-of-conduct-dialog.component.scss | 3 +++ ...ion-accept-code-of-conduct-dialog.component.ts | 15 +++++++++++++++ ...ersation-code-of-conduct-dialog.component.html | 7 +++++++ ...nversation-code-of-conduct-dialog.component.ts | 6 +++++- 7 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.html create mode 100644 src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.scss create mode 100644 src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.ts create mode 100644 src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.html diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts index 21ff3195384b..31c1f3340acb 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; +import { AfterViewInit, Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; import { ConversationDto } from 'app/entities/metis/conversation/conversation.model'; import { Post } from 'app/entities/metis/post.model'; import { ActivatedRoute, Router } from '@angular/router'; @@ -10,6 +10,7 @@ import { Course } from 'app/entities/course.model'; import { PageType } from 'app/shared/metis/metis.util'; import { BarControlConfiguration } from 'app/shared/tab-bar/tab-bar'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { ConversationAcceptCodeOfConductDialogComponent } from 'app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component'; import { ConversationCodeOfConductDialogComponent } from 'app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component'; @Component({ @@ -19,7 +20,7 @@ import { ConversationCodeOfConductDialogComponent } from 'app/overview/course-co encapsulation: ViewEncapsulation.None, providers: [MetisService], }) -export class CourseConversationsComponent implements OnInit, OnDestroy { +export class CourseConversationsComponent implements OnInit, OnDestroy, AfterViewInit { private ngUnsubscribe = new Subject(); course?: Course; isLoading = false; @@ -100,6 +101,8 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { if (this.controls) { this.controlConfiguration.subject!.next(this.controls); } + + this.openAcceptDialog(); } ngOnDestroy() { @@ -107,6 +110,11 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { this.ngUnsubscribe.complete(); } + openAcceptDialog() { + const modalRef: NgbModalRef = this.modalService.open(ConversationAcceptCodeOfConductDialogComponent, { backdrop: 'static', size: 'xl' }); + modalRef.componentInstance.codeOfConduct = this.course?.courseInformationSharingMessagingCodeOfConduct; + } + openDialog() { const modalRef: NgbModalRef = this.modalService.open(ConversationCodeOfConductDialogComponent, {}); modalRef.componentInstance.codeOfConduct = this.course?.courseInformationSharingMessagingCodeOfConduct; diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts index eb1d713fa9c0..ccec443a9671 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts @@ -32,6 +32,7 @@ import { OneToOneChatCreateDialogComponent } from './dialogs/one-to-one-chat-cre import { GroupChatCreateDialogComponent } from './dialogs/group-chat-create-dialog/group-chat-create-dialog.component'; import { GroupChatIconComponent } from './other/group-chat-icon/group-chat-icon.component'; import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; +import { ConversationAcceptCodeOfConductDialogComponent } from 'app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component'; const routes: Routes = [ { @@ -70,6 +71,7 @@ const routes: Routes = [ ConversationAddUsersDialogComponent, ConversationAddUsersFormComponent, ConversationMembersComponent, + ConversationAcceptCodeOfConductDialogComponent, ConversationCodeOfConductDialogComponent, ConversationDetailDialogComponent, ConversationInfoComponent, diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.html b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.html new file mode 100644 index 000000000000..de1d33d43b11 --- /dev/null +++ b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.html @@ -0,0 +1,6 @@ +
+ +
+ +
+
diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.scss b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.scss new file mode 100644 index 000000000000..c68788949311 --- /dev/null +++ b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.scss @@ -0,0 +1,3 @@ +.conversation-accept-code-of-conduct-dialog { + padding: 0.5rem; +} diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.ts new file mode 100644 index 000000000000..66b0dc658d7e --- /dev/null +++ b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.ts @@ -0,0 +1,15 @@ +import { Component, Input } from '@angular/core'; +import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component'; + +@Component({ + selector: 'jhi-conversation-accept-code-of-conduct-dialog', + templateUrl: `conversation-accept-code-of-conduct-dialog.component.html`, + styleUrls: ['./conversation-accept-code-of-conduct-dialog.component.scss'], +}) +export class ConversationAcceptCodeOfConductDialogComponent extends AbstractDialogComponent { + @Input() codeOfConduct: string; + + accept() { + this.close(); + } +} diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.html b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.html new file mode 100644 index 000000000000..4d9d23e130d4 --- /dev/null +++ b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.html @@ -0,0 +1,7 @@ +
+ + +
diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.ts index de863d20f6d0..0e293b5c9e65 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.ts @@ -3,8 +3,12 @@ import { AbstractDialogComponent } from 'app/overview/course-conversations/dialo @Component({ selector: 'jhi-conversation-code-of-conduct-dialog', - template: ` `, + templateUrl: './conversation-code-of-conduct-dialog.component.html', }) export class ConversationCodeOfConductDialogComponent extends AbstractDialogComponent { @Input() codeOfConduct: string; + + clear() { + this.close(); + } } From 93642b3bc1a098be16b1a4870b69454084ef2e91 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Tue, 5 Sep 2023 23:39:18 +0200 Subject: [PATCH 05/76] Make text area a markdown editor --- .../app/course/manage/course-update.component.html | 13 ++++++------- .../app/course/manage/course-update.component.ts | 8 ++++++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/webapp/app/course/manage/course-update.component.html b/src/main/webapp/app/course/manage/course-update.component.html index 9fed2d69e435..3dfb2630d0e7 100644 --- a/src/main/webapp/app/course/manage/course-update.component.html +++ b/src/main/webapp/app/course/manage/course-update.component.html @@ -348,13 +348,12 @@
class="text-secondary" ngbTooltip="{{ 'artemisApp.course.courseCommunicationSetting.messagingEnabled.codeOfConduct.tooltip' | artemisTranslate }}" > - + +
diff --git a/src/main/webapp/app/course/manage/course-update.component.ts b/src/main/webapp/app/course/manage/course-update.component.ts index 7ba326e35fd5..5638f7317177 100644 --- a/src/main/webapp/app/course/manage/course-update.component.ts +++ b/src/main/webapp/app/course/manage/course-update.component.ts @@ -520,6 +520,14 @@ export class CourseUpdateComponent implements OnInit { this.courseForm.controls['registrationConfirmationMessage'].setValue(message); } + /** + * Updates courseInformationSharingMessagingCodeOfConduct on markdown change + * @param message new courseInformationSharingMessagingCodeOfConduct + */ + updateCourseInformationSharingMessagingCodeOfConduct(message: string) { + this.courseForm.controls['courseInformationSharingMessagingCodeOfConduct'].setValue(message); + } + /** * Auxiliary method checking if online course is currently true */ From 11814414c99da6e51496b2a865e1782207b77ff1 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 6 Sep 2023 11:46:17 +0200 Subject: [PATCH 06/76] Build back dialogs and review change requests --- .../manage/course-update.component.html | 6 +--- .../course-conversations.component.html | 4 --- .../course-conversations.component.ts | 35 ++----------------- .../course-conversations.module.ts | 6 ---- ...cept-code-of-conduct-dialog.component.html | 6 ---- ...cept-code-of-conduct-dialog.component.scss | 3 -- ...accept-code-of-conduct-dialog.component.ts | 15 -------- ...tion-code-of-conduct-dialog.component.html | 7 ---- ...sation-code-of-conduct-dialog.component.ts | 14 -------- src/main/webapp/i18n/de/conversation.json | 4 +-- src/main/webapp/i18n/de/course.json | 2 +- src/main/webapp/i18n/en/conversation.json | 2 +- src/main/webapp/i18n/en/course.json | 2 +- 13 files changed, 8 insertions(+), 98 deletions(-) delete mode 100644 src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.html delete mode 100644 src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.scss delete mode 100644 src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.ts delete mode 100644 src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.html delete mode 100644 src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.ts diff --git a/src/main/webapp/app/course/manage/course-update.component.html b/src/main/webapp/app/course/manage/course-update.component.html index 3dfb2630d0e7..cab968b69610 100644 --- a/src/main/webapp/app/course/manage/course-update.component.html +++ b/src/main/webapp/app/course/manage/course-update.component.html @@ -343,11 +343,7 @@
for="field_messagingCodeOfConduct" >Messaging Code of Conduct - + - - -
diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts index 31c1f3340acb..95354c04a880 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import { ConversationDto } from 'app/entities/metis/conversation/conversation.model'; import { Post } from 'app/entities/metis/post.model'; import { ActivatedRoute, Router } from '@angular/router'; @@ -8,10 +8,6 @@ import { getAsChannelDto } from 'app/entities/metis/conversation/channel.model'; import { MetisService } from 'app/shared/metis/metis.service'; import { Course } from 'app/entities/course.model'; import { PageType } from 'app/shared/metis/metis.util'; -import { BarControlConfiguration } from 'app/shared/tab-bar/tab-bar'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { ConversationAcceptCodeOfConductDialogComponent } from 'app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component'; -import { ConversationCodeOfConductDialogComponent } from 'app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component'; @Component({ selector: 'jhi-course-conversations', @@ -20,7 +16,7 @@ import { ConversationCodeOfConductDialogComponent } from 'app/overview/course-co encapsulation: ViewEncapsulation.None, providers: [MetisService], }) -export class CourseConversationsComponent implements OnInit, OnDestroy, AfterViewInit { +export class CourseConversationsComponent implements OnInit, OnDestroy { private ngUnsubscribe = new Subject(); course?: Course; isLoading = false; @@ -29,20 +25,12 @@ export class CourseConversationsComponent implements OnInit, OnDestroy, AfterVie activeConversation?: ConversationDto = undefined; conversationsOfUser: ConversationDto[] = []; - // The extracted controls template from our template to be rendered in the top bar of "CourseOverviewComponent" - @ViewChild('controls', { static: false }) private controls: TemplateRef; - // Provides the control configuration to be read and used by "CourseOverviewComponent" - public readonly controlConfiguration: BarControlConfiguration = { - subject: new Subject>(), - }; - // MetisConversationService is created in course overview, so we can use it here constructor( private router: Router, private activatedRoute: ActivatedRoute, public metisConversationService: MetisConversationService, public metisService: MetisService, - private modalService: NgbModal, ) {} getAsChannel = getAsChannelDto; @@ -96,30 +84,11 @@ export class CourseConversationsComponent implements OnInit, OnDestroy, AfterVie }); } - ngAfterViewInit(): void { - // Send our controls template to parent so it will be rendered in the top bar - if (this.controls) { - this.controlConfiguration.subject!.next(this.controls); - } - - this.openAcceptDialog(); - } - ngOnDestroy() { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); } - openAcceptDialog() { - const modalRef: NgbModalRef = this.modalService.open(ConversationAcceptCodeOfConductDialogComponent, { backdrop: 'static', size: 'xl' }); - modalRef.componentInstance.codeOfConduct = this.course?.courseInformationSharingMessagingCodeOfConduct; - } - - openDialog() { - const modalRef: NgbModalRef = this.modalService.open(ConversationCodeOfConductDialogComponent, {}); - modalRef.componentInstance.codeOfConduct = this.course?.courseInformationSharingMessagingCodeOfConduct; - } - private subscribeToActiveConversation() { this.metisConversationService.activeConversation$.pipe(takeUntil(this.ngUnsubscribe)).subscribe((conversation: ConversationDto) => { this.activeConversation = conversation; diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts index ccec443a9671..5aac80556033 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts @@ -20,7 +20,6 @@ import { ConversationAddUsersDialogComponent } from './dialogs/conversation-add- import { ConversationAddUsersFormComponent } from './dialogs/conversation-add-users-dialog/add-users-form/conversation-add-users-form.component'; import { CourseUsersSelectorModule } from 'app/shared/course-users-selector/course-users-selector.module'; import { ConversationMembersComponent } from './dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-members.component'; -import { ConversationCodeOfConductDialogComponent } from 'app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component'; import { ConversationDetailDialogComponent } from './dialogs/conversation-detail-dialog/conversation-detail-dialog.component'; import { ConversationInfoComponent } from './dialogs/conversation-detail-dialog/tabs/conversation-info/conversation-info.component'; import { ConversationMemberRowComponent } from './dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component'; @@ -31,8 +30,6 @@ import { ConversationSidebarEntryComponent } from './layout/conversation-selecti import { OneToOneChatCreateDialogComponent } from './dialogs/one-to-one-chat-create-dialog/one-to-one-chat-create-dialog.component'; import { GroupChatCreateDialogComponent } from './dialogs/group-chat-create-dialog/group-chat-create-dialog.component'; import { GroupChatIconComponent } from './other/group-chat-icon/group-chat-icon.component'; -import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; -import { ConversationAcceptCodeOfConductDialogComponent } from 'app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component'; const routes: Routes = [ { @@ -54,7 +51,6 @@ const routes: Routes = [ ArtemisDataTableModule, InfiniteScrollModule, CourseUsersSelectorModule, - ArtemisMarkdownModule, ], declarations: [ CourseConversationsComponent, @@ -71,8 +67,6 @@ const routes: Routes = [ ConversationAddUsersDialogComponent, ConversationAddUsersFormComponent, ConversationMembersComponent, - ConversationAcceptCodeOfConductDialogComponent, - ConversationCodeOfConductDialogComponent, ConversationDetailDialogComponent, ConversationInfoComponent, ConversationMemberRowComponent, diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.html b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.html deleted file mode 100644 index de1d33d43b11..000000000000 --- a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.html +++ /dev/null @@ -1,6 +0,0 @@ -
- -
- -
-
diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.scss b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.scss deleted file mode 100644 index c68788949311..000000000000 --- a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -.conversation-accept-code-of-conduct-dialog { - padding: 0.5rem; -} diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.ts deleted file mode 100644 index 66b0dc658d7e..000000000000 --- a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/accept/conversation-accept-code-of-conduct-dialog.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component'; - -@Component({ - selector: 'jhi-conversation-accept-code-of-conduct-dialog', - templateUrl: `conversation-accept-code-of-conduct-dialog.component.html`, - styleUrls: ['./conversation-accept-code-of-conduct-dialog.component.scss'], -}) -export class ConversationAcceptCodeOfConductDialogComponent extends AbstractDialogComponent { - @Input() codeOfConduct: string; - - accept() { - this.close(); - } -} diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.html b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.html deleted file mode 100644 index 4d9d23e130d4..000000000000 --- a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.html +++ /dev/null @@ -1,7 +0,0 @@ -
- - -
diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.ts deleted file mode 100644 index 0e293b5c9e65..000000000000 --- a/src/main/webapp/app/overview/course-conversations/dialogs/code-of-conduct/conversation-code-of-conduct-dialog.component.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component'; - -@Component({ - selector: 'jhi-conversation-code-of-conduct-dialog', - templateUrl: './conversation-code-of-conduct-dialog.component.html', -}) -export class ConversationCodeOfConductDialogComponent extends AbstractDialogComponent { - @Input() codeOfConduct: string; - - clear() { - this.close(); - } -} diff --git a/src/main/webapp/i18n/de/conversation.json b/src/main/webapp/i18n/de/conversation.json index f2f6a8c4b430..9d6a30a2bd3b 100644 --- a/src/main/webapp/i18n/de/conversation.json +++ b/src/main/webapp/i18n/de/conversation.json @@ -10,8 +10,8 @@ }, "conversation": { "codeOfConduct": { - "topBar": { - "button": "Verhaltens-Kodex" + "sideBar": { + "button": "Verhaltenskodex" } } }, diff --git a/src/main/webapp/i18n/de/course.json b/src/main/webapp/i18n/de/course.json index 95cdb5ec75f1..efc1c081ef96 100644 --- a/src/main/webapp/i18n/de/course.json +++ b/src/main/webapp/i18n/de/course.json @@ -92,7 +92,7 @@ "tooltip": "Ermöglicht den Nachrichtenaustausch zwischen Nutzer:innen in privaten oder öffentlichen Kanälen, Gruppenchats oder Direktnachrichten. Kanäle können nur von Lehrenden und Tutor:innen erstellt werden. Nutzer:innen können selbst öffentlichen Kanälen beitreten und müssen zu privaten Kanälen hinzugefügt werden. Alle Nutzer:innen können einen privaten Gruppenchat starten und andere Nutzer:innen hinzufügen. Ein Gruppenchat ist auf zehn Mitglieder:innen begrenzt. Alle Nutzer:innen können Direktnachrichten an andere Nutzer:innen senden. Die Chats finden im Nachrichtenbereich des Kurses statt.", "codeOfConduct": { "label": "Nachrichten-Verhaltenskodex", - "tooltip": "Der Nachrichten-Verhaltenskodex gibt Nutzer:innen an, wie sie miteinander kollaborieren sollen und welche Konsequenzen bei Fehlverhalten drohen können, sowie einen Kontakt zur Berichterstattung." + "tooltip": "Der Nachrichten-Verhaltenskodex gibt Nutzer:innen an, wie sie miteinander kommunizieren sollen und welche Konsequenzen bei Fehlverhalten drohen können, sowie einen Kontakt zur Berichterstattung." } } }, diff --git a/src/main/webapp/i18n/en/conversation.json b/src/main/webapp/i18n/en/conversation.json index ddc612abcd3b..6e6d357a2af9 100644 --- a/src/main/webapp/i18n/en/conversation.json +++ b/src/main/webapp/i18n/en/conversation.json @@ -10,7 +10,7 @@ }, "conversation": { "codeOfConduct": { - "topBar": { + "sideBar": { "button": "Code of Conduct" } } diff --git a/src/main/webapp/i18n/en/course.json b/src/main/webapp/i18n/en/course.json index e2ddcbdd4820..a9b73c1a3930 100644 --- a/src/main/webapp/i18n/en/course.json +++ b/src/main/webapp/i18n/en/course.json @@ -92,7 +92,7 @@ "tooltip": "Enables messaging between course users in private or public channels, group chats or direct messages. Channels can only be created by instructors and tutors. Users can self-join public channels and must be invited to private channels. Every user can start a private group chat and add other users. A group chat is limited to 10 members. Every user can start a private one-to-one chat with another user. The chats happens in the messaging space of the course.", "codeOfConduct": { "label": "Messages Code of Conduct", - "tooltip": "The Messages Code of Conduct describes to users how best to collaborate and which consequences might be raised if there is misconduct, as well as, contact information for reporting." + "tooltip": "The Messages Code of Conduct describes to users how best to communicate and which consequences might be raised if there is misconduct, as well as, contact information for reporting." } } }, From 427566bc778aa9aacf1cddd2fb584a84d820de8c Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 6 Sep 2023 11:49:19 +0200 Subject: [PATCH 07/76] Supplement 11814414c99da6e51496b2a865e1782207b77ff1 --- .../webapp/app/course/manage/course-update.component.html | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/webapp/app/course/manage/course-update.component.html b/src/main/webapp/app/course/manage/course-update.component.html index cab968b69610..f77ec2bd30d0 100644 --- a/src/main/webapp/app/course/manage/course-update.component.html +++ b/src/main/webapp/app/course/manage/course-update.component.html @@ -337,10 +337,7 @@
>
- From c12f78704f5ffd222d9abc307e65ad737f67b0d9 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 7 Sep 2023 17:16:07 +0200 Subject: [PATCH 08/76] On review --- .../course-conversations/course-conversations.component.ts | 1 - src/main/webapp/i18n/de/conversation.json | 7 ------- src/main/webapp/i18n/de/course.json | 4 ++-- src/main/webapp/i18n/en/conversation.json | 7 ------- src/main/webapp/i18n/en/course.json | 4 ++-- 5 files changed, 4 insertions(+), 19 deletions(-) diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts index 95354c04a880..b4ae773043ae 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts @@ -24,7 +24,6 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { postInThread?: Post; activeConversation?: ConversationDto = undefined; conversationsOfUser: ConversationDto[] = []; - // MetisConversationService is created in course overview, so we can use it here constructor( private router: Router, diff --git a/src/main/webapp/i18n/de/conversation.json b/src/main/webapp/i18n/de/conversation.json index 9d6a30a2bd3b..57a010abfd4a 100644 --- a/src/main/webapp/i18n/de/conversation.json +++ b/src/main/webapp/i18n/de/conversation.json @@ -8,13 +8,6 @@ "mainParagraph": "Gib im Chat keine vertraulichen Informationen wie Passwörter, Kreditkartennummern oder persönliche Identifikationsnummern weiter. Diese Informationen sind sensibel und könnten für Identitätsdiebstahl oder Betrug verwendet werden, wenn sie in die falschen Hände geraten.", "lastParagraph": "Bewahre deine persönlichen Daten sicher auf, indem du sie für dich behältst und sie nicht online weitergibst." }, - "conversation": { - "codeOfConduct": { - "sideBar": { - "button": "Verhaltenskodex" - } - } - }, "conversationsLayout": { "tabTitle": "Kursnachrichten", "breadCrumbLabel": "Nachrichten", diff --git a/src/main/webapp/i18n/de/course.json b/src/main/webapp/i18n/de/course.json index efc1c081ef96..b67ff604f30c 100644 --- a/src/main/webapp/i18n/de/course.json +++ b/src/main/webapp/i18n/de/course.json @@ -91,8 +91,8 @@ "label": "Nachrichten aktiviert", "tooltip": "Ermöglicht den Nachrichtenaustausch zwischen Nutzer:innen in privaten oder öffentlichen Kanälen, Gruppenchats oder Direktnachrichten. Kanäle können nur von Lehrenden und Tutor:innen erstellt werden. Nutzer:innen können selbst öffentlichen Kanälen beitreten und müssen zu privaten Kanälen hinzugefügt werden. Alle Nutzer:innen können einen privaten Gruppenchat starten und andere Nutzer:innen hinzufügen. Ein Gruppenchat ist auf zehn Mitglieder:innen begrenzt. Alle Nutzer:innen können Direktnachrichten an andere Nutzer:innen senden. Die Chats finden im Nachrichtenbereich des Kurses statt.", "codeOfConduct": { - "label": "Nachrichten-Verhaltenskodex", - "tooltip": "Der Nachrichten-Verhaltenskodex gibt Nutzer:innen an, wie sie miteinander kommunizieren sollen und welche Konsequenzen bei Fehlverhalten drohen können, sowie einen Kontakt zur Berichterstattung." + "label": "Verhaltenskodex", + "tooltip": "Der Verhaltenskodex gibt Nutzer:innen an, wie sie miteinander kommunizieren sollen und welche Konsequenzen bei Fehlverhalten drohen können, sowie einen Kontakt zur Berichterstattung." } } }, diff --git a/src/main/webapp/i18n/en/conversation.json b/src/main/webapp/i18n/en/conversation.json index 6e6d357a2af9..dc8a6f15fb6b 100644 --- a/src/main/webapp/i18n/en/conversation.json +++ b/src/main/webapp/i18n/en/conversation.json @@ -8,13 +8,6 @@ "mainParagraph": "Do not share confidential information like passwords, credit card numbers, or personal identification numbers in the chat. This information is sensitive and could potentially be used for identity theft or fraud if it falls into the wrong hands.", "lastParagraph": "Keep your personal information safe by keeping it to yourself and not sharing it online." }, - "conversation": { - "codeOfConduct": { - "sideBar": { - "button": "Code of Conduct" - } - } - }, "conversationsLayout": { "tabTitle": "Course Messages", "breadCrumbLabel": "Messages", diff --git a/src/main/webapp/i18n/en/course.json b/src/main/webapp/i18n/en/course.json index a9b73c1a3930..d1a12700b2c1 100644 --- a/src/main/webapp/i18n/en/course.json +++ b/src/main/webapp/i18n/en/course.json @@ -91,8 +91,8 @@ "label": "Messaging Enabled", "tooltip": "Enables messaging between course users in private or public channels, group chats or direct messages. Channels can only be created by instructors and tutors. Users can self-join public channels and must be invited to private channels. Every user can start a private group chat and add other users. A group chat is limited to 10 members. Every user can start a private one-to-one chat with another user. The chats happens in the messaging space of the course.", "codeOfConduct": { - "label": "Messages Code of Conduct", - "tooltip": "The Messages Code of Conduct describes to users how best to communicate and which consequences might be raised if there is misconduct, as well as, contact information for reporting." + "label": "Code of Conduct", + "tooltip": "The Code of Conduct describes to users how best to communicate and which consequences might be raised if there is misconduct, as well as, contact information for reporting." } } }, From 8ed563fbc41a8e7b75aef287c3b4ada0e1570989 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 7 Sep 2023 17:25:10 +0200 Subject: [PATCH 09/76] Fix tests https://bamboo.ase.in.tum.de/browse/ARTEMIS-TESTS5355-TSTEST-3 --- .../spec/component/course/course-update.component.spec.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/javascript/spec/component/course/course-update.component.spec.ts b/src/test/javascript/spec/component/course/course-update.component.spec.ts index ee5ab992b8b4..fb9371444530 100644 --- a/src/test/javascript/spec/component/course/course-update.component.spec.ts +++ b/src/test/javascript/spec/component/course/course-update.component.spec.ts @@ -14,6 +14,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { HasAnyAuthorityDirective } from 'app/shared/auth/has-any-authority.directive'; import { ColorSelectorComponent } from 'app/shared/color-selector/color-selector.component'; import { FormDateTimePickerComponent } from 'app/shared/date-time-picker/date-time-picker.component'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; import { SecuredImageComponent } from 'app/shared/image/secured-image.component'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockComponent, MockDirective, MockModule, MockPipe, MockProvider } from 'ng-mocks'; @@ -105,12 +106,13 @@ describe('Course Management Update Component', () => { declarations: [ CourseUpdateComponent, MarkdownEditorStubComponent, - MockPipe(ArtemisTranslatePipe), - MockComponent(SecuredImageComponent), - MockComponent(FormDateTimePickerComponent), MockComponent(ColorSelectorComponent), + MockComponent(FormDateTimePickerComponent), + MockComponent(HelpIconComponent), + MockComponent(SecuredImageComponent), MockDirective(HasAnyAuthorityDirective), MockDirective(TranslateDirective), + MockPipe(ArtemisTranslatePipe), MockPipe(RemoveKeysPipe), ], }) From be7c8e46b3e0a9e6dc0dfd5a37b285ef347e73b9 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 7 Sep 2023 23:02:41 +0200 Subject: [PATCH 10/76] Add change at end --- ...0230830183901_changelog.xml => 20230907225501_changelog.xml} | 2 +- src/main/resources/config/liquibase/master.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/main/resources/config/liquibase/changelog/{20230830183901_changelog.xml => 20230907225501_changelog.xml} (91%) diff --git a/src/main/resources/config/liquibase/changelog/20230830183901_changelog.xml b/src/main/resources/config/liquibase/changelog/20230907225501_changelog.xml similarity index 91% rename from src/main/resources/config/liquibase/changelog/20230830183901_changelog.xml rename to src/main/resources/config/liquibase/changelog/20230907225501_changelog.xml index 8f326ee7627e..6ce1a1de6303 100644 --- a/src/main/resources/config/liquibase/changelog/20230830183901_changelog.xml +++ b/src/main/resources/config/liquibase/changelog/20230907225501_changelog.xml @@ -2,7 +2,7 @@ - + diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml index 1e1ffe9315df..58b65f6f3059 100644 --- a/src/main/resources/config/liquibase/master.xml +++ b/src/main/resources/config/liquibase/master.xml @@ -50,8 +50,8 @@ - + From db38d8b361acd239b3845c1fa272b50aea62e5ad Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 7 Sep 2023 23:26:49 +0200 Subject: [PATCH 11/76] Add code of conduct to conversation resource --- .../conversation/ConversationResource.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java index 80829e9ed7e6..7facf4d55fdc 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java @@ -124,6 +124,36 @@ public ResponseEntity hasUnreadMessages(@PathVariable Long courseId) { return ResponseEntity.ok(conversationService.userHasUnreadMessages(courseId, requestingUser)); } + /** + * GET /api/courses/:courseId/accept-code-of-conduct : Checks if the user accepted the code of conduct + * + * @param courseId + * @return ResponseEntity with status 200 (Ok) and if the user accepted the course's code of conduct + */ + @GetMapping("/{courseId}/code-of-conduct") + @EnforceAtLeastStudent + public ResponseEntity isCodeOfConductAccepted(@PathVariable Long courseId) { + checkMessagingEnabledElseThrow(courseId); + var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); + authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, courseRepository.findByIdElseThrow(courseId), requestingUser); + return ResponseEntity.ok(false); + } + + /** + * POST /api/courses/:courseId/accept-code-of-conduct : Accept the course's code of conduct + * + * @param courseId + * @return ResponseEntity with status 200 (Ok) and if the user accepted the code of conduct + */ + @GetMapping("/{courseId}/code-of-conduct") + @EnforceAtLeastStudent + public ResponseEntity acceptCodeOfConduct(@PathVariable Long courseId) { + checkMessagingEnabledElseThrow(courseId); + var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); + authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, courseRepository.findByIdElseThrow(courseId), requestingUser); + return ResponseEntity.ok(true); + } + /** * GET /api/courses/:courseId/conversations/:conversationId/members/search: Searches for members of a conversation * From 0fd19489025344726773f5f44ef4bf10c9f5a844 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 7 Sep 2023 23:37:34 +0200 Subject: [PATCH 12/76] On review --- .../rest/metis/conversation/ConversationResource.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java index 7facf4d55fdc..ed742f8becd8 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java @@ -125,10 +125,10 @@ public ResponseEntity hasUnreadMessages(@PathVariable Long courseId) { } /** - * GET /api/courses/:courseId/accept-code-of-conduct : Checks if the user accepted the code of conduct + * GET /api/courses/:courseId/code-of-conduct : Checks if the user accepted the code of conduct * * @param courseId - * @return ResponseEntity with status 200 (Ok) and if the user accepted the course's code of conduct + * @return ResponseEntity with status 200 (Ok) and body is true if the user accepted the course's code of conduct */ @GetMapping("/{courseId}/code-of-conduct") @EnforceAtLeastStudent @@ -140,12 +140,12 @@ public ResponseEntity isCodeOfConductAccepted(@PathVariable Long course } /** - * POST /api/courses/:courseId/accept-code-of-conduct : Accept the course's code of conduct + * POST /api/courses/:courseId/code-of-conduct : Accept the course's code of conduct * * @param courseId - * @return ResponseEntity with status 200 (Ok) and if the user accepted the code of conduct + * @return ResponseEntity with status 200 (Ok) and body is true if the user accepted the code of conduct */ - @GetMapping("/{courseId}/code-of-conduct") + @PostMapping("/{courseId}/code-of-conduct") @EnforceAtLeastStudent public ResponseEntity acceptCodeOfConduct(@PathVariable Long courseId) { checkMessagingEnabledElseThrow(courseId); From d97f655264073e9ce8a3defc007a82520970c59f Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Fri, 8 Sep 2023 05:19:37 +0200 Subject: [PATCH 13/76] Persist code of conduct agreement --- .../artemis/domain/CourseCodeOfConduct.java | 50 +++++++++++++++++++ .../CourseCodeOfConductRepository.java | 21 ++++++++ .../conversation/ConversationResource.java | 32 ++++++++++-- .../changelog/20230908050001_changelog.xml | 15 ++++++ .../resources/config/liquibase/master.xml | 1 + 5 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConduct.java create mode 100644 src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductRepository.java create mode 100644 src/main/resources/config/liquibase/changelog/20230908050001_changelog.xml diff --git a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConduct.java b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConduct.java new file mode 100644 index 000000000000..1834d90edece --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConduct.java @@ -0,0 +1,50 @@ +package de.tum.in.www1.artemis.domain; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonIncludeProperties; + +@Entity +@Table(name = "course_code_of_conduct") +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class CourseCodeOfConduct extends DomainObject { + + @ManyToOne + @JsonIncludeProperties({ "id" }) + @NotNull + private Course course; + + @ManyToOne + @JsonIncludeProperties({ "id" }) + @NotNull + private User user; + + @Column(name = "is_code_of_conduct_accepted") + private Boolean isCodeOfConductAccepted; + + public Course getCourse() { + return course; + } + + public void setCourse(Course course) { + this.course = course; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public Boolean getIsCodeOfConductAccepted() { + return isCodeOfConductAccepted; + } + + public void setIsCodeOfConductAccepted(Boolean isCodeOfConductAccepted) { + this.isCodeOfConductAccepted = isCodeOfConductAccepted; + } +} diff --git a/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductRepository.java new file mode 100644 index 000000000000..f4a644ef54f3 --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductRepository.java @@ -0,0 +1,21 @@ +package de.tum.in.www1.artemis.repository; + +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import de.tum.in.www1.artemis.domain.CourseCodeOfConduct; + +@Repository +public interface CourseCodeOfConductRepository extends JpaRepository { + + @Query(""" + SELECT c + FROM CourseCodeOfConduct c + WHERE c.course.id = :courseId AND c.user.id = :userId + """) + Optional findByCourseIdAndUserId(@Param("courseId") Long courseId, @Param("userId") Long userId); +} diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java index ed742f8becd8..9b1502aa5fcc 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java @@ -15,8 +15,11 @@ import org.springframework.web.server.ResponseStatusException; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import de.tum.in.www1.artemis.domain.Course; +import de.tum.in.www1.artemis.domain.CourseCodeOfConduct; import de.tum.in.www1.artemis.domain.metis.conversation.Channel; import de.tum.in.www1.artemis.domain.metis.conversation.Conversation; +import de.tum.in.www1.artemis.repository.CourseCodeOfConductRepository; import de.tum.in.www1.artemis.repository.CourseRepository; import de.tum.in.www1.artemis.repository.UserRepository; import de.tum.in.www1.artemis.security.Role; @@ -44,14 +47,18 @@ public class ConversationResource extends ConversationManagementResource { private final AuthorizationCheckService authorizationCheckService; + private final CourseCodeOfConductRepository courseCodeOfConductRepository; + private final UserRepository userRepository; public ConversationResource(ConversationService conversationService, ChannelAuthorizationService channelAuthorizationService, - AuthorizationCheckService authorizationCheckService, UserRepository userRepository, CourseRepository courseRepository) { + AuthorizationCheckService authorizationCheckService, UserRepository userRepository, CourseRepository courseRepository, + CourseCodeOfConductRepository courseCodeOfConductRepository) { super(courseRepository); this.conversationService = conversationService; this.channelAuthorizationService = channelAuthorizationService; this.authorizationCheckService = authorizationCheckService; + this.courseCodeOfConductRepository = courseCodeOfConductRepository; this.userRepository = userRepository; } @@ -136,7 +143,13 @@ public ResponseEntity isCodeOfConductAccepted(@PathVariable Long course checkMessagingEnabledElseThrow(courseId); var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, courseRepository.findByIdElseThrow(courseId), requestingUser); - return ResponseEntity.ok(false); + Optional courseCodeOfConduct = courseCodeOfConductRepository.findByCourseIdAndUserId(courseId, requestingUser.getId()); + if (courseCodeOfConduct.isPresent()) { + return ResponseEntity.ok(courseCodeOfConduct.get().getIsCodeOfConductAccepted()); + } + else { + return ResponseEntity.ok(false); + } } /** @@ -151,7 +164,20 @@ public ResponseEntity acceptCodeOfConduct(@PathVariable Long courseId) checkMessagingEnabledElseThrow(courseId); var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, courseRepository.findByIdElseThrow(courseId), requestingUser); - return ResponseEntity.ok(true); + Optional courseCodeOfConduct = courseCodeOfConductRepository.findByCourseIdAndUserId(courseId, requestingUser.getId()); + if (courseCodeOfConduct.isPresent()) { + CourseCodeOfConduct courseCodeOfConductSafe = courseCodeOfConduct.get(); + courseCodeOfConductSafe.setIsCodeOfConductAccepted(true); + return ResponseEntity.ok(courseCodeOfConductRepository.save(courseCodeOfConductSafe).getIsCodeOfConductAccepted()); + } + else { + CourseCodeOfConduct courseCodeOfConductSafe = new CourseCodeOfConduct(); + Course course = courseRepository.findByIdElseThrow(courseId); + courseCodeOfConductSafe.setCourse(course); + courseCodeOfConductSafe.setUser(requestingUser); + courseCodeOfConductSafe.setIsCodeOfConductAccepted(true); + return ResponseEntity.ok(false); + } } /** diff --git a/src/main/resources/config/liquibase/changelog/20230908050001_changelog.xml b/src/main/resources/config/liquibase/changelog/20230908050001_changelog.xml new file mode 100644 index 000000000000..a88b7bce5fa7 --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/20230908050001_changelog.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml index 58b65f6f3059..7bf62b60e78a 100644 --- a/src/main/resources/config/liquibase/master.xml +++ b/src/main/resources/config/liquibase/master.xml @@ -52,6 +52,7 @@ + From c021ede09e99ce0f2f0dd5d57696b36e3e0957ab Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Fri, 8 Sep 2023 05:45:43 +0200 Subject: [PATCH 14/76] Add UI --- .../course-conversations.component.html | 11 +++- .../course-conversations.component.ts | 32 ++++++++++ .../course-conversations.module.ts | 2 + ...versation-selection-sidebar.component.html | 2 + ...onversation-selection-sidebar.component.ts | 4 ++ .../conversations/conversation.service.ts | 8 +++ .../metis/metis-conversation.service.ts | 59 ++++++++++++++++++- 7 files changed, 115 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.html b/src/main/webapp/app/overview/course-conversations/course-conversations.component.html index 06764d4c21ec..898400280d2d 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.html +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.html @@ -1,8 +1,17 @@ -
+
+

{{ 'artemisApp.conversation.codeOfConduct.sideBar.button' | artemisTranslate }}

+
+ +
+
+
+

{{ 'artemisApp.conversation.codeOfConduct.sideBar.button' | artemisTranslate }}

+
+
diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts index b4ae773043ae..a637073854b8 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts @@ -24,6 +24,18 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { postInThread?: Post; activeConversation?: ConversationDto = undefined; conversationsOfUser: ConversationDto[] = []; + + isCodeOfConductAccepted: boolean = false; + isCodeOfConductPresented: boolean = false; + + get codeOfConduct(): string { + if (this.course?.courseInformationSharingMessagingCodeOfConduct) { + return this.course?.courseInformationSharingMessagingCodeOfConduct; + } else { + return ''; + } + } + // MetisConversationService is created in course overview, so we can use it here constructor( private router: Router, @@ -56,6 +68,8 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { this.subscribeToQueryParameter(); // service is fully set up, now we can subscribe to the respective observables this.subscribeToActiveConversation(); + this.subscribeToIsCodeOfConductAccepted(); + this.subscribeToIsCodeOfConductPresented(); this.subscribeToConversationsOfUser(); this.subscribeToLoading(); this.isServiceSetUp = true; @@ -96,6 +110,18 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { }); } + private subscribeToIsCodeOfConductAccepted() { + this.metisConversationService.isCodeOfConductAccepted$.pipe(takeUntil(this.ngUnsubscribe)).subscribe((isCodeOfConductAccepted: boolean) => { + this.isCodeOfConductAccepted = isCodeOfConductAccepted; + }); + } + + private subscribeToIsCodeOfConductPresented() { + this.metisConversationService.isCodeOfConductPresented$.pipe(takeUntil(this.ngUnsubscribe)).subscribe((isCodeOfConductPresented: boolean) => { + this.isCodeOfConductPresented = isCodeOfConductPresented; + }); + } + private subscribeToConversationsOfUser() { this.metisConversationService.conversationsOfUser$.pipe(takeUntil(this.ngUnsubscribe)).subscribe((conversations: ConversationDto[]) => { this.conversationsOfUser = conversations ?? []; @@ -107,4 +133,10 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { this.isLoading = isLoading; }); } + + acceptCodeOfConduct() { + if (this.course) { + this.metisConversationService.acceptCodeOfConduct(this.course); + } + } } diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts index 5aac80556033..c988788843d7 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts @@ -30,6 +30,7 @@ import { ConversationSidebarEntryComponent } from './layout/conversation-selecti import { OneToOneChatCreateDialogComponent } from './dialogs/one-to-one-chat-create-dialog/one-to-one-chat-create-dialog.component'; import { GroupChatCreateDialogComponent } from './dialogs/group-chat-create-dialog/group-chat-create-dialog.component'; import { GroupChatIconComponent } from './other/group-chat-icon/group-chat-icon.component'; +import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; const routes: Routes = [ { @@ -46,6 +47,7 @@ const routes: Routes = [ imports: [ RouterModule.forChild(routes), MetisModule, + ArtemisMarkdownModule, ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisDataTableModule, diff --git a/src/main/webapp/app/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.html b/src/main/webapp/app/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.html index e26e347181d5..c4a7c21a6e86 100644 --- a/src/main/webapp/app/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.html +++ b/src/main/webapp/app/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.html @@ -198,6 +198,8 @@

+ +
diff --git a/src/main/webapp/app/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.ts b/src/main/webapp/app/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.ts index 1d583d42d5cc..7020f2d02509 100644 --- a/src/main/webapp/app/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.ts +++ b/src/main/webapp/app/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.ts @@ -354,4 +354,8 @@ export class ConversationSelectionSidebarComponent implements AfterViewInit, OnI private filterChannelsOfType(subType: ChannelSubType): ChannelDTO[] { return this.displayedChannelConversations.filter((channel) => channel.subType === subType); } + + openCodeOfConduct() { + this.metisConversationService.setCodeOfConduct(); + } } diff --git a/src/main/webapp/app/shared/metis/conversations/conversation.service.ts b/src/main/webapp/app/shared/metis/conversations/conversation.service.ts index 2e03a5d2c00c..c352a4610234 100644 --- a/src/main/webapp/app/shared/metis/conversations/conversation.service.ts +++ b/src/main/webapp/app/shared/metis/conversations/conversation.service.ts @@ -122,6 +122,14 @@ export class ConversationService { return this.http.get(`${this.resourceUrl}${courseId}/unread-messages`, { observe: 'response' }); } + acceptCodeOfConduct(courseId: number): Observable> { + return this.http.post(`${this.resourceUrl}${courseId}/code-of-conduct`, null, { observe: 'response' }); + } + + checkIfCodeOfConductIsAccepted(courseId: number): Observable> { + return this.http.get(`${this.resourceUrl}${courseId}/code-of-conduct`, { observe: 'response' }); + } + public convertDateFromClient = (conversation: Conversation) => ({ ...conversation, creationDate: convertDateFromClient(conversation.creationDate), 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 08f1f69dc2d9..44eb9b6b724f 100644 --- a/src/main/webapp/app/shared/metis/metis-conversation.service.ts +++ b/src/main/webapp/app/shared/metis/metis-conversation.service.ts @@ -31,6 +31,10 @@ export class MetisConversationService implements OnDestroy { // Stores the currently selected conversation private activeConversation: ConversationDto | undefined = undefined; _activeConversation$: ReplaySubject = new ReplaySubject(1); + private isCodeOfConductAccepted: boolean = false; + _isCodeOfConductAccepted$: ReplaySubject = new ReplaySubject(1); + private isCodeOfConductPresented: boolean = false; + _isCodeOfConductPresented$: ReplaySubject = new ReplaySubject(1); private hasUnreadMessages = false; _hasUnreadMessages$: Subject = new ReplaySubject(1); // Stores the course for which the service is setup -> should not change during the lifetime of the service @@ -74,6 +78,12 @@ export class MetisConversationService implements OnDestroy { get activeConversation$(): Observable { return this._activeConversation$.asObservable(); } + get isCodeOfConductAccepted$(): Observable { + return this._isCodeOfConductAccepted$.asObservable(); + } + get isCodeOfConductPresented$(): Observable { + return this._isCodeOfConductPresented$.asObservable(); + } get hasUnreadMessages$(): Observable { return this._hasUnreadMessages$.asObservable(); } @@ -90,7 +100,7 @@ export class MetisConversationService implements OnDestroy { return this._isLoading$.asObservable(); } - public setActiveConversation = (conversationIdentifier: ConversationDto | number | undefined) => { + public setActiveConversation(conversationIdentifier: ConversationDto | number | undefined) { this.updateLastReadDateAndNumberOfUnreadMessages(); let cachedConversation = undefined; if (conversationIdentifier) { @@ -104,7 +114,16 @@ export class MetisConversationService implements OnDestroy { } this.activeConversation = cachedConversation; this._activeConversation$.next(this.activeConversation); - }; + this.isCodeOfConductPresented = false; + this._isCodeOfConductPresented$.next(this.isCodeOfConductPresented); + } + + public setCodeOfConduct() { + this.activeConversation = undefined; + this._activeConversation$.next(this.activeConversation); + this.isCodeOfConductPresented = true; + this._isCodeOfConductPresented$.next(this.isCodeOfConductPresented); + } private updateLastReadDateAndNumberOfUnreadMessages() { // update last read date and number of unread messages of the conversation that is currently active before switching to another conversation @@ -239,6 +258,42 @@ export class MetisConversationService implements OnDestroy { }); }; + acceptCodeOfConduct(course: Course) { + if (!course?.id) { + return; + } + + this.conversationService.acceptCodeOfConduct(course.id).subscribe({ + next: (response) => { + if (response.body !== null) { + this.isCodeOfConductAccepted = response.body; + this._isCodeOfConductAccepted$.next(this.isCodeOfConductAccepted); + } + }, + error: (errorResponse: HttpErrorResponse) => { + onError(this.alertService, errorResponse); + }, + }); + } + + checkIsCodeOfConductAccepted(course: Course) { + if (!course?.id) { + return; + } + + this.conversationService.checkIfCodeOfConductIsAccepted(course.id).subscribe({ + next: (response) => { + if (response.body !== null) { + this.isCodeOfConductAccepted = response.body; + this._isCodeOfConductAccepted$.next(this.isCodeOfConductAccepted); + } + }, + error: (errorResponse: HttpErrorResponse) => { + onError(this.alertService, errorResponse); + }, + }); + } + private hasUnreadMessagesCheck = (): void => { const hasNewMessages = this.conversationsOfUser.some((conversation) => { return conversation?.unreadMessagesCount && conversation.unreadMessagesCount > 0; From f679bb70853c7af3e95dfe99011d79d467067222 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Mon, 11 Sep 2023 10:17:45 +0200 Subject: [PATCH 15/76] Localization --- .../app/course/manage/course-update.component.html | 6 ++---- .../course-conversations.component.html | 6 +++--- .../conversation-selection-sidebar.component.html | 2 +- src/main/webapp/i18n/de/course.json | 6 +----- src/main/webapp/i18n/de/courseOfConduct.json | 9 +++++++++ src/main/webapp/i18n/en/course.json | 6 +----- src/main/webapp/i18n/en/courseOfConduct.json | 9 +++++++++ 7 files changed, 26 insertions(+), 18 deletions(-) create mode 100644 src/main/webapp/i18n/de/courseOfConduct.json create mode 100644 src/main/webapp/i18n/en/courseOfConduct.json diff --git a/src/main/webapp/app/course/manage/course-update.component.html b/src/main/webapp/app/course/manage/course-update.component.html index b943661e4fa1..16bb0cf7c7fd 100644 --- a/src/main/webapp/app/course/manage/course-update.component.html +++ b/src/main/webapp/app/course/manage/course-update.component.html @@ -337,10 +337,8 @@
>
- - + +
-

{{ 'artemisApp.conversation.codeOfConduct.sideBar.button' | artemisTranslate }}

+

{{ 'artemisApp.codeOfConduct.title' | artemisTranslate }}

- +
-

{{ 'artemisApp.conversation.codeOfConduct.sideBar.button' | artemisTranslate }}

+

{{ 'artemisApp.codeOfConduct.title' | artemisTranslate }}

diff --git a/src/main/webapp/app/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.html b/src/main/webapp/app/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.html index c4a7c21a6e86..870e9868917d 100644 --- a/src/main/webapp/app/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.html +++ b/src/main/webapp/app/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.html @@ -199,7 +199,7 @@

- +
diff --git a/src/main/webapp/i18n/de/course.json b/src/main/webapp/i18n/de/course.json index 3390637f6d32..642561a24be8 100644 --- a/src/main/webapp/i18n/de/course.json +++ b/src/main/webapp/i18n/de/course.json @@ -93,11 +93,7 @@ }, "messagingEnabled": { "label": "Nachrichten aktiviert", - "tooltip": "Ermöglicht den Nachrichtenaustausch zwischen Nutzer:innen in privaten oder öffentlichen Kanälen, Gruppenchats oder Direktnachrichten. Kanäle können nur von Lehrenden und Tutor:innen erstellt werden. Nutzer:innen können selbst öffentlichen Kanälen beitreten und müssen zu privaten Kanälen hinzugefügt werden. Alle Nutzer:innen können einen privaten Gruppenchat starten und andere Nutzer:innen hinzufügen. Ein Gruppenchat ist auf zehn Mitglieder:innen begrenzt. Alle Nutzer:innen können Direktnachrichten an andere Nutzer:innen senden. Die Chats finden im Nachrichtenbereich des Kurses statt.", - "codeOfConduct": { - "label": "Verhaltenskodex", - "tooltip": "Der Verhaltenskodex gibt Nutzer:innen an, wie sie miteinander kommunizieren sollen und welche Konsequenzen bei Fehlverhalten drohen können, sowie einen Kontakt zur Berichterstattung." - } + "tooltip": "Ermöglicht den Nachrichtenaustausch zwischen Nutzer:innen in privaten oder öffentlichen Kanälen, Gruppenchats oder Direktnachrichten. Kanäle können nur von Lehrenden und Tutor:innen erstellt werden. Nutzer:innen können selbst öffentlichen Kanälen beitreten und müssen zu privaten Kanälen hinzugefügt werden. Alle Nutzer:innen können einen privaten Gruppenchat starten und andere Nutzer:innen hinzufügen. Ein Gruppenchat ist auf zehn Mitglieder:innen begrenzt. Alle Nutzer:innen können Direktnachrichten an andere Nutzer:innen senden. Die Chats finden im Nachrichtenbereich des Kurses statt." } }, "registrationEnabled": { diff --git a/src/main/webapp/i18n/de/courseOfConduct.json b/src/main/webapp/i18n/de/courseOfConduct.json new file mode 100644 index 000000000000..52cd1b76335a --- /dev/null +++ b/src/main/webapp/i18n/de/courseOfConduct.json @@ -0,0 +1,9 @@ +{ + "artemisApp": { + "codeOfConduct": { + "accept": "Akzeptieren", + "title": "Verhaltenskodex", + "tooltip": "Der Verhaltenskodex gibt Nutzer:innen an, wie sie miteinander kommunizieren sollen und welche Konsequenzen bei Fehlverhalten drohen können, sowie einen Kontakt zur Berichterstattung." + } + } +} diff --git a/src/main/webapp/i18n/en/course.json b/src/main/webapp/i18n/en/course.json index 6e8695f31d42..978785393729 100644 --- a/src/main/webapp/i18n/en/course.json +++ b/src/main/webapp/i18n/en/course.json @@ -93,11 +93,7 @@ }, "messagingEnabled": { "label": "Messaging Enabled", - "tooltip": "Enables messaging between course users in private or public channels, group chats or direct messages. Channels can only be created by instructors and tutors. Users can self-join public channels and must be invited to private channels. Every user can start a private group chat and add other users. A group chat is limited to 10 members. Every user can start a private one-to-one chat with another user. The chats happens in the messaging space of the course.", - "codeOfConduct": { - "label": "Code of Conduct", - "tooltip": "The Code of Conduct describes to users how best to communicate and which consequences might be raised if there is misconduct, as well as, contact information for reporting." - } + "tooltip": "Enables messaging between course users in private or public channels, group chats or direct messages. Channels can only be created by instructors and tutors. Users can self-join public channels and must be invited to private channels. Every user can start a private group chat and add other users. A group chat is limited to 10 members. Every user can start a private one-to-one chat with another user. The chats happens in the messaging space of the course." } }, "registrationEnabled": { diff --git a/src/main/webapp/i18n/en/courseOfConduct.json b/src/main/webapp/i18n/en/courseOfConduct.json new file mode 100644 index 000000000000..fb4aaa24e7ae --- /dev/null +++ b/src/main/webapp/i18n/en/courseOfConduct.json @@ -0,0 +1,9 @@ +{ + "artemisApp": { + "codeOfConduct": { + "accept": "Accept", + "title": "Code of Conduct", + "tooltip": "The Code of Conduct describes to users how best to communicate and which consequences might be raised if there is misconduct, as well as, contact information for reporting." + } + } +} From 655cc751cf96a4f785185e1814d507b73e00cb92 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Mon, 11 Sep 2023 10:24:53 +0200 Subject: [PATCH 16/76] Create CourseCodeOfConductService --- .../service/CourseCodeOfConductService.java | 59 +++++++++++++++++++ .../conversation/ConversationResource.java | 36 +++-------- 2 files changed, 68 insertions(+), 27 deletions(-) create mode 100644 src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductService.java diff --git a/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductService.java b/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductService.java new file mode 100644 index 000000000000..52058c0133e8 --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductService.java @@ -0,0 +1,59 @@ +package de.tum.in.www1.artemis.service; + +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import de.tum.in.www1.artemis.domain.Course; +import de.tum.in.www1.artemis.domain.CourseCodeOfConduct; +import de.tum.in.www1.artemis.domain.User; +import de.tum.in.www1.artemis.repository.CourseCodeOfConductRepository; + +/** + * Service Implementation for managing a course's code of conduct. + */ +@Service +public class CourseCodeOfConductService { + + private final CourseCodeOfConductRepository courseCodeOfConductRepository; + + CourseCodeOfConductService(CourseCodeOfConductRepository courseCodeOfConductRepository) { + this.courseCodeOfConductRepository = courseCodeOfConductRepository; + } + + /** + * Fetches if a user agreed to a course's code of conduct. + * + * @param user the user in the course + * @param course the code of conduct's course + * @return if the user agreed to the course's code of conduct + */ + public boolean fetchUserAgreesToCodeOfConductInCourse(User user, Course course) { + Optional courseCodeOfConduct = courseCodeOfConductRepository.findByCourseIdAndUserId(course.getId(), user.getId()); + if (courseCodeOfConduct.isPresent()) { + return courseCodeOfConduct.get().getIsCodeOfConductAccepted(); + } + else { + CourseCodeOfConduct courseCodeOfConductNew = new CourseCodeOfConduct(); + courseCodeOfConductNew.setCourse(course); + courseCodeOfConductNew.setUser(user); + courseCodeOfConductNew.setIsCodeOfConductAccepted(false); + courseCodeOfConductRepository.save(courseCodeOfConductNew); + return courseCodeOfConductNew.getIsCodeOfConductAccepted(); + } + } + + /** + * A user agrees to a course's code of conduct. + * + * @param user the user in the course + * @param course the code of conduct's course + */ + public void setUserAgreesToCodeOfConductInCourse(User user, Course course) { + CourseCodeOfConduct courseCodeOfConduct = courseCodeOfConductRepository.findByCourseIdAndUserId(course.getId(), user.getId()).orElse(new CourseCodeOfConduct()); + courseCodeOfConduct.setCourse(course); + courseCodeOfConduct.setUser(user); + courseCodeOfConduct.setIsCodeOfConductAccepted(true); + courseCodeOfConductRepository.save(courseCodeOfConduct); + } +} diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java index 9b1502aa5fcc..f0b6b5e3ce0f 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java @@ -15,16 +15,14 @@ import org.springframework.web.server.ResponseStatusException; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import de.tum.in.www1.artemis.domain.Course; -import de.tum.in.www1.artemis.domain.CourseCodeOfConduct; import de.tum.in.www1.artemis.domain.metis.conversation.Channel; import de.tum.in.www1.artemis.domain.metis.conversation.Conversation; -import de.tum.in.www1.artemis.repository.CourseCodeOfConductRepository; import de.tum.in.www1.artemis.repository.CourseRepository; import de.tum.in.www1.artemis.repository.UserRepository; import de.tum.in.www1.artemis.security.Role; import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastStudent; import de.tum.in.www1.artemis.service.AuthorizationCheckService; +import de.tum.in.www1.artemis.service.CourseCodeOfConductService; import de.tum.in.www1.artemis.service.dto.UserPublicInfoDTO; import de.tum.in.www1.artemis.service.metis.conversation.ConversationService; import de.tum.in.www1.artemis.service.metis.conversation.ConversationService.ConversationMemberSearchFilters; @@ -47,18 +45,18 @@ public class ConversationResource extends ConversationManagementResource { private final AuthorizationCheckService authorizationCheckService; - private final CourseCodeOfConductRepository courseCodeOfConductRepository; + private final CourseCodeOfConductService courseCodeOfConductService; private final UserRepository userRepository; public ConversationResource(ConversationService conversationService, ChannelAuthorizationService channelAuthorizationService, AuthorizationCheckService authorizationCheckService, UserRepository userRepository, CourseRepository courseRepository, - CourseCodeOfConductRepository courseCodeOfConductRepository) { + CourseCodeOfConductService courseCodeOfConductService) { super(courseRepository); this.conversationService = conversationService; this.channelAuthorizationService = channelAuthorizationService; this.authorizationCheckService = authorizationCheckService; - this.courseCodeOfConductRepository = courseCodeOfConductRepository; + this.courseCodeOfConductService = courseCodeOfConductService; this.userRepository = userRepository; } @@ -141,15 +139,10 @@ public ResponseEntity hasUnreadMessages(@PathVariable Long courseId) { @EnforceAtLeastStudent public ResponseEntity isCodeOfConductAccepted(@PathVariable Long courseId) { checkMessagingEnabledElseThrow(courseId); + var course = courseRepository.findByIdElseThrow(courseId); var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, courseRepository.findByIdElseThrow(courseId), requestingUser); - Optional courseCodeOfConduct = courseCodeOfConductRepository.findByCourseIdAndUserId(courseId, requestingUser.getId()); - if (courseCodeOfConduct.isPresent()) { - return ResponseEntity.ok(courseCodeOfConduct.get().getIsCodeOfConductAccepted()); - } - else { - return ResponseEntity.ok(false); - } + return ResponseEntity.ok(courseCodeOfConductService.fetchUserAgreesToCodeOfConductInCourse(requestingUser, course)); } /** @@ -162,22 +155,11 @@ public ResponseEntity isCodeOfConductAccepted(@PathVariable Long course @EnforceAtLeastStudent public ResponseEntity acceptCodeOfConduct(@PathVariable Long courseId) { checkMessagingEnabledElseThrow(courseId); + var course = courseRepository.findByIdElseThrow(courseId); var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, courseRepository.findByIdElseThrow(courseId), requestingUser); - Optional courseCodeOfConduct = courseCodeOfConductRepository.findByCourseIdAndUserId(courseId, requestingUser.getId()); - if (courseCodeOfConduct.isPresent()) { - CourseCodeOfConduct courseCodeOfConductSafe = courseCodeOfConduct.get(); - courseCodeOfConductSafe.setIsCodeOfConductAccepted(true); - return ResponseEntity.ok(courseCodeOfConductRepository.save(courseCodeOfConductSafe).getIsCodeOfConductAccepted()); - } - else { - CourseCodeOfConduct courseCodeOfConductSafe = new CourseCodeOfConduct(); - Course course = courseRepository.findByIdElseThrow(courseId); - courseCodeOfConductSafe.setCourse(course); - courseCodeOfConductSafe.setUser(requestingUser); - courseCodeOfConductSafe.setIsCodeOfConductAccepted(true); - return ResponseEntity.ok(false); - } + courseCodeOfConductService.setUserAgreesToCodeOfConductInCourse(requestingUser, course); + return ResponseEntity.ok(courseCodeOfConductService.fetchUserAgreesToCodeOfConductInCourse(requestingUser, course)); } /** From 92fe2512cb0f645e27e42706f9c1a1714a76ef03 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Mon, 11 Sep 2023 10:32:05 +0200 Subject: [PATCH 17/76] Add change log at end --- src/main/resources/config/liquibase/master.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml index 9140dd701bf0..103a5319cfba 100644 --- a/src/main/resources/config/liquibase/master.xml +++ b/src/main/resources/config/liquibase/master.xml @@ -52,8 +52,8 @@ - + From d3fd1c29ab61b96f98c5a99bca85c6ac87f86fbd Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Mon, 11 Sep 2023 11:35:26 +0200 Subject: [PATCH 18/76] Create CourseCodeOfConductServiceTest --- .../CourseCodeOfConductServiceTest.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductServiceTest.java diff --git a/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductServiceTest.java b/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductServiceTest.java new file mode 100644 index 000000000000..a0db261c9ad3 --- /dev/null +++ b/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductServiceTest.java @@ -0,0 +1,51 @@ +package de.tum.in.www1.artemis.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.ZonedDateTime; +import java.util.HashSet; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import de.tum.in.www1.artemis.AbstractSpringIntegrationBambooBitbucketJiraTest; +import de.tum.in.www1.artemis.course.CourseFactory; +import de.tum.in.www1.artemis.repository.CourseRepository; +import de.tum.in.www1.artemis.user.UserUtilService; + +public class CourseCodeOfConductServiceTest extends AbstractSpringIntegrationBambooBitbucketJiraTest { + + private static final String TEST_PREFIX = "coursecodeofconductservice"; + + private static final ZonedDateTime pastTimestamp = ZonedDateTime.now().minusDays(1); + + private static final ZonedDateTime futureTimestamp = ZonedDateTime.now().plusDays(1); + + @Autowired + private CourseRepository courseRepo; + + @Autowired + private CourseCodeOfConductService courseCodeOfConductService; + + @Autowired + private UserUtilService userUtilService; + + @BeforeEach + void init() { + userUtilService.addUsers(TEST_PREFIX, 1, 0, 0, 0); + } + + @Test + void fetchAndAgreeIsCodeOfConductAccepted() { + var user = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); + var course = CourseFactory.generateCourse(null, pastTimestamp, futureTimestamp, new HashSet<>(), "student", "tutor", "editor", "instructor"); + courseRepo.save(course); + var resultBeforeAgreement = courseCodeOfConductService.fetchUserAgreesToCodeOfConductInCourse(user, course); + assertThat(resultBeforeAgreement).isFalse(); + + courseCodeOfConductService.setUserAgreesToCodeOfConductInCourse(user, course); + var resultAfterAgreement = courseCodeOfConductService.fetchUserAgreesToCodeOfConductInCourse(user, course); + assertThat(resultAfterAgreement).isTrue(); + } +} From c9c46cdc89b65cc370ce1c248260bef75fb885f7 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Mon, 11 Sep 2023 13:52:53 +0200 Subject: [PATCH 19/76] On review --- .../in/www1/artemis/domain/CourseCodeOfConduct.java | 8 +++++++- .../repository/CourseCodeOfConductRepository.java | 10 ++++++++++ .../rest/metis/conversation/ConversationResource.java | 10 +++++----- src/main/resources/config/liquibase/master.xml | 3 +-- .../service/CourseCodeOfConductServiceTest.java | 8 ++++---- 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConduct.java b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConduct.java index 1834d90edece..4b8527aa49d8 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConduct.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConduct.java @@ -1,11 +1,17 @@ package de.tum.in.www1.artemis.domain; -import javax.persistence.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.ManyToOne; +import javax.persistence.Table; import javax.validation.constraints.NotNull; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonIncludeProperties; +/** + * A user's agreement of a course's code of conduct. + */ @Entity @Table(name = "course_code_of_conduct") @JsonInclude(JsonInclude.Include.NON_EMPTY) diff --git a/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductRepository.java index f4a644ef54f3..44833317c26d 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductRepository.java @@ -9,9 +9,19 @@ import de.tum.in.www1.artemis.domain.CourseCodeOfConduct; +/** + * Spring Data repository for the Code of Conduct entity. + */ @Repository public interface CourseCodeOfConductRepository extends JpaRepository { + /** + * Find the user's agreement to a course's code of conduct. + * + * @param courseId the ID of the code of conduct's course + * @param userId the user's ID + * @return the user's agreement to the course's code of conduct + */ @Query(""" SELECT c FROM CourseCodeOfConduct c diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java index f0b6b5e3ce0f..39e7a68e303b 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java @@ -130,10 +130,10 @@ public ResponseEntity hasUnreadMessages(@PathVariable Long courseId) { } /** - * GET /api/courses/:courseId/code-of-conduct : Checks if the user accepted the code of conduct + * GET /api/courses/:courseId/code-of-conduct : Checks if the user agrees to the code of conduct * - * @param courseId - * @return ResponseEntity with status 200 (Ok) and body is true if the user accepted the course's code of conduct + * @param courseId the course's ID + * @return ResponseEntity with status 200 (Ok) and body is true if the user agreed to the course's code of conduct */ @GetMapping("/{courseId}/code-of-conduct") @EnforceAtLeastStudent @@ -148,8 +148,8 @@ public ResponseEntity isCodeOfConductAccepted(@PathVariable Long course /** * POST /api/courses/:courseId/code-of-conduct : Accept the course's code of conduct * - * @param courseId - * @return ResponseEntity with status 200 (Ok) and body is true if the user accepted the code of conduct + * @param courseId the course's ID + * @return ResponseEntity with status 200 (Ok) and body is true if the user agreed to the code of conduct */ @PostMapping("/{courseId}/code-of-conduct") @EnforceAtLeastStudent diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml index 3e94cc9f33c4..67e16ad24f35 100644 --- a/src/main/resources/config/liquibase/master.xml +++ b/src/main/resources/config/liquibase/master.xml @@ -52,10 +52,9 @@ - - + diff --git a/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductServiceTest.java b/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductServiceTest.java index a0db261c9ad3..952bbd1ff8fe 100644 --- a/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductServiceTest.java +++ b/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductServiceTest.java @@ -14,13 +14,13 @@ import de.tum.in.www1.artemis.repository.CourseRepository; import de.tum.in.www1.artemis.user.UserUtilService; -public class CourseCodeOfConductServiceTest extends AbstractSpringIntegrationBambooBitbucketJiraTest { +class CourseCodeOfConductServiceTest extends AbstractSpringIntegrationBambooBitbucketJiraTest { private static final String TEST_PREFIX = "coursecodeofconductservice"; - private static final ZonedDateTime pastTimestamp = ZonedDateTime.now().minusDays(1); + private static final ZonedDateTime PastTimestamp = ZonedDateTime.now().minusDays(1); - private static final ZonedDateTime futureTimestamp = ZonedDateTime.now().plusDays(1); + private static final ZonedDateTime FutureTimestamp = ZonedDateTime.now().plusDays(1); @Autowired private CourseRepository courseRepo; @@ -39,7 +39,7 @@ void init() { @Test void fetchAndAgreeIsCodeOfConductAccepted() { var user = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); - var course = CourseFactory.generateCourse(null, pastTimestamp, futureTimestamp, new HashSet<>(), "student", "tutor", "editor", "instructor"); + var course = CourseFactory.generateCourse(null, PastTimestamp, FutureTimestamp, new HashSet<>(), "student", "tutor", "editor", "instructor"); courseRepo.save(course); var resultBeforeAgreement = courseCodeOfConductService.fetchUserAgreesToCodeOfConductInCourse(user, course); assertThat(resultBeforeAgreement).isFalse(); From fadaaae9b34071c3deed2ac93d9f7bade572c370 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Tue, 12 Sep 2023 15:18:57 +0200 Subject: [PATCH 20/76] Add code of conduct template --- .../www1/artemis/web/rest/FileResource.java | 15 +++++++++ .../templates/codeofconduct/README.md | 32 +++++++++++++++++++ .../in/www1/artemis/FileIntegrationTest.java | 7 ++++ 3 files changed, 54 insertions(+) create mode 100644 src/main/resources/templates/codeofconduct/README.md diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java index 7cd98dca5191..4622c1e28308 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java @@ -304,6 +304,21 @@ public ResponseEntity getCourseIcon(@PathVariable Long courseId) { return responseEntityForFilePath(fileService.actualPathForPublicPath(course.getCourseIcon())); } + /** + * GET /files/course/code-of-conduct/template : Get the Code of Conduct template + * + * @return The requested file, 403 if the logged-in user is not allowed to access it, or 404 if the file doesn't exist + */ + @GetMapping("files/templates/code-of-conduct") + @EnforceAtLeastInstructor + public ResponseEntity getCourseCodeOfConduct() throws IOException { + var templatePath = Path.of("templates", "codeofconduct", "README.md"); + log.debug("REST request to get template : {}", templatePath); + var resource = resourceLoaderService.getResource(templatePath); + var path = resource.getFile().getPath(); + return responseEntityForFilePath(path); + } + /** * GET /files/exam-user/signatures/:examUserId/:filename : Get the exam user signature * diff --git a/src/main/resources/templates/codeofconduct/README.md b/src/main/resources/templates/codeofconduct/README.md new file mode 100644 index 000000000000..be6379e3489e --- /dev/null +++ b/src/main/resources/templates/codeofconduct/README.md @@ -0,0 +1,32 @@ +# Template: Adapt to your demands + +We as students, tutors, and instructors pledge to make participation in our course a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. + +## Our Standards + +### Examples of behavior that contributes to a positive environment for our community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall community + +### Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Scope + +This Code of Conduct applies within all communication channels. + +## Reporting + +Each course is represented by instructors. If you see inappropriate behavior or content, please report it. +You may find a list of contacts responsible for this course below. diff --git a/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java index 1348aad63fa2..ff7109d7ced6 100644 --- a/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java @@ -121,6 +121,13 @@ void testGetCourseIcon() throws Exception { assertThat(receivedIcon).isEqualTo("some data"); } + @Test + @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") + void testGetCourseCodeOfConductTemplate() throws Exception { + var template = request.get("/api/files/templates/code-of-conduct", HttpStatus.OK, String.class); + assertThat(template).startsWith("# Template"); + } + @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void testGetDragAndDropBackgroundFile() throws Exception { From b167d05dba0ba063ec772d0cfdaf9c06486fa490 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Tue, 12 Sep 2023 15:24:51 +0200 Subject: [PATCH 21/76] Add pipes --- .../course-conversations.component.spec.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts index 2e8fa240a513..9d4bc9efe8c9 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angul import { ConversationDto } from 'app/entities/metis/conversation/conversation.model'; import { generateExampleChannelDTO, generateExampleGroupChatDTO, generateOneToOneChatDTO } from './helpers/conversationExampleModels'; import { AlertService } from 'app/core/util/alert.service'; -import { MockComponent, MockProvider } from 'ng-mocks'; +import { MockComponent, MockPipe, MockProvider } from 'ng-mocks'; import { MetisConversationService } from 'app/shared/metis/metis-conversation.service'; import { LoadingIndicatorContainerStubComponent } from '../../../helpers/stubs/loading-indicator-container-stub.component'; import { ConversationSelectionSidebarComponent } from 'app/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component'; @@ -18,6 +18,9 @@ import { ActivatedRoute, Params, Router, convertToParamMap } from '@angular/rout import { MockRouter } from '../../../helpers/mocks/mock-router'; import { MetisService } from 'app/shared/metis/metis.service'; import { Post } from 'app/entities/metis/post.model'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; + const examples: (ConversationDto | undefined)[] = [undefined, generateOneToOneChatDTO({}), generateExampleGroupChatDTO({}), generateExampleChannelDTO({})]; examples.forEach((activeConversation) => { @@ -43,6 +46,8 @@ examples.forEach((activeConversation) => { MockComponent(ConversationHeaderComponent), MockComponent(ConversationMessagesComponent), MockComponent(ConversationThreadSidebarComponent), + MockPipe(ArtemisTranslatePipe), + MockPipe(HtmlForMarkdownPipe), ], providers: [ MockProvider(AlertService), From cf79d14708264d53ae928459617384e66cff87b1 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Tue, 12 Sep 2023 17:09:16 +0200 Subject: [PATCH 22/76] Fix localization --- src/main/webapp/app/course/manage/course-update.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/app/course/manage/course-update.component.html b/src/main/webapp/app/course/manage/course-update.component.html index 16bb0cf7c7fd..86bad6318f81 100644 --- a/src/main/webapp/app/course/manage/course-update.component.html +++ b/src/main/webapp/app/course/manage/course-update.component.html @@ -337,7 +337,7 @@
>
- + Date: Tue, 12 Sep 2023 17:25:20 +0200 Subject: [PATCH 23/76] Prototype load template --- .../webapp/app/course/manage/course-admin.service.ts | 4 ++++ .../app/course/manage/course-update.component.ts | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/main/webapp/app/course/manage/course-admin.service.ts b/src/main/webapp/app/course/manage/course-admin.service.ts index fe7ceef20670..c467280309fc 100644 --- a/src/main/webapp/app/course/manage/course-admin.service.ts +++ b/src/main/webapp/app/course/manage/course-admin.service.ts @@ -51,4 +51,8 @@ export class CourseAdminService { delete(courseId: number): Observable> { return this.http.delete(`${this.resourceUrl}/${courseId}`, { observe: 'response' }); } + + codeOfConductTemplate(): Observable> { + return this.http.get(`api/files/templates/code-of-conduct`, { observe: 'response', responseType: 'text' as 'json' }); + } } diff --git a/src/main/webapp/app/course/manage/course-update.component.ts b/src/main/webapp/app/course/manage/course-update.component.ts index 315fe2e8c2d1..197f2b20e76d 100644 --- a/src/main/webapp/app/course/manage/course-update.component.ts +++ b/src/main/webapp/app/course/manage/course-update.component.ts @@ -110,6 +110,17 @@ export class CourseUpdateComponent implements OnInit { this.course.maxComplaintTextLimit! > 0 && this.course.maxComplaintResponseTextLimit! > 0; this.requestMoreFeedbackEnabled = this.course.maxRequestMoreFeedbackTimeDays! > 0; + } else { + this.courseAdminService.codeOfConductTemplate().subscribe({ + next: (value: HttpResponse) => { + if (value.body) { + this.course.courseInformationSharingMessagingCodeOfConduct = value.body; + } + }, + error: (err: any) => { + console.log(err); + }, + }); } }); From 94d59cab2dc27b21aa661ac1c08877ba40f5c98b Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Tue, 12 Sep 2023 21:57:56 +0200 Subject: [PATCH 24/76] Move getTemplateCodeOfCondcut --- .../app/course/manage/course-admin.service.ts | 4 ---- .../app/course/manage/course-update.component.ts | 15 ++++++++------- src/main/webapp/app/shared/http/file.service.ts | 13 +++++++++++-- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/main/webapp/app/course/manage/course-admin.service.ts b/src/main/webapp/app/course/manage/course-admin.service.ts index c467280309fc..fe7ceef20670 100644 --- a/src/main/webapp/app/course/manage/course-admin.service.ts +++ b/src/main/webapp/app/course/manage/course-admin.service.ts @@ -51,8 +51,4 @@ export class CourseAdminService { delete(courseId: number): Observable> { return this.http.delete(`${this.resourceUrl}/${courseId}`, { observe: 'response' }); } - - codeOfConductTemplate(): Observable> { - return this.http.get(`api/files/templates/code-of-conduct`, { observe: 'response', responseType: 'text' as 'json' }); - } } diff --git a/src/main/webapp/app/course/manage/course-update.component.ts b/src/main/webapp/app/course/manage/course-update.component.ts index 197f2b20e76d..9b8f23346f68 100644 --- a/src/main/webapp/app/course/manage/course-update.component.ts +++ b/src/main/webapp/app/course/manage/course-update.component.ts @@ -27,6 +27,8 @@ import { CourseAdminService } from 'app/course/manage/course-admin.service'; import { FeatureToggle, FeatureToggleService } from 'app/shared/feature-toggle/feature-toggle.service'; import { AccountService } from 'app/core/auth/account.service'; import { EventManager } from 'app/core/util/event-manager.service'; +import { FileService } from 'app/shared/http/file.service'; +import { onError } from 'app/shared/util/global.utils'; @Component({ selector: 'jhi-course-update', @@ -79,6 +81,7 @@ export class CourseUpdateComponent implements OnInit { private courseAdminService: CourseAdminService, private activatedRoute: ActivatedRoute, private fileUploaderService: FileUploaderService, + private fileService: FileService, private alertService: AlertService, private profileService: ProfileService, private organizationService: OrganizationManagementService, @@ -111,15 +114,13 @@ export class CourseUpdateComponent implements OnInit { this.course.maxComplaintResponseTextLimit! > 0; this.requestMoreFeedbackEnabled = this.course.maxRequestMoreFeedbackTimeDays! > 0; } else { - this.courseAdminService.codeOfConductTemplate().subscribe({ - next: (value: HttpResponse) => { - if (value.body) { - this.course.courseInformationSharingMessagingCodeOfConduct = value.body; + this.fileService.getTemplateCodeOfCondcut().subscribe({ + next: (res: HttpResponse) => { + if (res.body) { + this.course.courseInformationSharingMessagingCodeOfConduct = res.body; } }, - error: (err: any) => { - console.log(err); - }, + error: (res: HttpErrorResponse) => onError(this.alertService, res), }); } }); diff --git a/src/main/webapp/app/shared/http/file.service.ts b/src/main/webapp/app/shared/http/file.service.ts index b1013c9dd322..45de081ecf95 100644 --- a/src/main/webapp/app/shared/http/file.service.ts +++ b/src/main/webapp/app/shared/http/file.service.ts @@ -1,5 +1,6 @@ -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; import { ProgrammingLanguage, ProjectType } from 'app/entities/programming-exercise.model'; @@ -15,7 +16,7 @@ export class FileService { * @param {ProjectType} projectType (if available) * @returns json test file */ - getTemplateFile(language: ProgrammingLanguage, projectType?: ProjectType) { + getTemplateFile(language: ProgrammingLanguage, projectType?: ProjectType): Observable { const urlParts: string[] = [language]; if (projectType) { urlParts.push(projectType); @@ -23,6 +24,14 @@ export class FileService { return this.http.get(`${this.resourceUrl}/templates/` + urlParts.join('/'), { responseType: 'text' as 'json' }); } + /** + * Fetches the template code of conduct + * @returns markdown file + */ + getTemplateCodeOfCondcut(): Observable> { + return this.http.get(`api/files/templates/code-of-conduct`, { observe: 'response', responseType: 'text' as 'json' }); + } + /** * Downloads the file from the provided downloadUrl. * From a3de4bcc65230c6f52d32caeacb057bc49595734 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 13 Sep 2023 10:06:51 +0200 Subject: [PATCH 25/76] Fix localization --- src/main/webapp/app/course/manage/course-update.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/app/course/manage/course-update.component.html b/src/main/webapp/app/course/manage/course-update.component.html index 86bad6318f81..48db7aa224f5 100644 --- a/src/main/webapp/app/course/manage/course-update.component.html +++ b/src/main/webapp/app/course/manage/course-update.component.html @@ -338,7 +338,7 @@
- + Date: Wed, 13 Sep 2023 13:21:23 +0200 Subject: [PATCH 26/76] List responsible contacts --- .../service/dto/UserPublicInfoDTO.java | 11 ++++ src/main/webapp/app/core/user/user.model.ts | 1 + ...nversations-code-of-conduct.component.html | 9 ++++ ...conversations-code-of-conduct.component.ts | 54 +++++++++++++++++++ .../course-conversations.component.html | 10 ++-- .../course-conversations.module.ts | 2 + 6 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html create mode 100644 src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts diff --git a/src/main/java/de/tum/in/www1/artemis/service/dto/UserPublicInfoDTO.java b/src/main/java/de/tum/in/www1/artemis/service/dto/UserPublicInfoDTO.java index 73f432f608c0..80a36881ee09 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/dto/UserPublicInfoDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/dto/UserPublicInfoDTO.java @@ -25,6 +25,8 @@ public class UserPublicInfoDTO { private String lastName; + private String email; + private Boolean isInstructor; private Boolean isEditor; @@ -43,6 +45,7 @@ public UserPublicInfoDTO(User user) { this.name = user.getName(); this.firstName = user.getFirstName(); this.lastName = user.getLastName(); + this.email = user.getEmail(); } /** @@ -101,6 +104,14 @@ public void setLastName(String lastName) { this.lastName = lastName; } + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + public Boolean getIsInstructor() { return isInstructor; } diff --git a/src/main/webapp/app/core/user/user.model.ts b/src/main/webapp/app/core/user/user.model.ts index cb34cc387861..1599dd14d74c 100644 --- a/src/main/webapp/app/core/user/user.model.ts +++ b/src/main/webapp/app/core/user/user.model.ts @@ -56,6 +56,7 @@ export class UserPublicInfoDTO { public name?: string; public firstName?: string; public lastName?: string; + public email?: string; public isInstructor?: boolean; public isEditor?: boolean; public isTeachingAssistant?: boolean; diff --git a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html new file mode 100644 index 000000000000..bda05813c9e2 --- /dev/null +++ b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html @@ -0,0 +1,9 @@ +

{{ 'artemisApp.codeOfConduct.title' | artemisTranslate }}

+
+ diff --git a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts new file mode 100644 index 000000000000..52b82b81b69f --- /dev/null +++ b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts @@ -0,0 +1,54 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { UserPublicInfoDTO } from 'app/core/user/user.model'; +import { CourseManagementService } from 'app/course/manage/course-management.service'; +import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; +import { onError } from 'app/shared/util/global.utils'; +import { AlertService } from 'app/core/util/alert.service'; +import { Course } from 'app/entities/course.model'; + +@Component({ + selector: 'jhi-course-conversations-code-of-conduct', + templateUrl: './course-conversations-code-of-conduct.component.html', +}) +export class CourseConversationsCodeOfConductComponent implements OnInit { + @Input() + course: Course; + + get codeOfConduct(): string { + if (this.course.courseInformationSharingMessagingCodeOfConduct) { + return this.course.courseInformationSharingMessagingCodeOfConduct; + } + return ''; + } + + responsibleContacts: UserPublicInfoDTO[] = []; + + constructor( + private alertService: AlertService, + private courseManagementService: CourseManagementService, + ) {} + + ngOnInit() { + if (this.course.id) { + this.courseManagementService.searchUsers(this.course.id, '', ['instructors']).subscribe({ + next: (res: HttpResponse) => { + if (res.body) { + this.responsibleContacts = res.body; + } + }, + error: (res: HttpErrorResponse) => onError(this.alertService, res), + }); + } + } + + getUserLabel(user: UserPublicInfoDTO) { + let label = ''; + if (user.firstName) { + label += user.firstName + ' '; + } + if (user.lastName) { + label += user.lastName + ' '; + } + return label.trim(); + } +} diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.html b/src/main/webapp/app/overview/course-conversations/course-conversations.component.html index 782db8d466e1..f3c5dece5f14 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.html +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.html @@ -1,7 +1,8 @@
-

{{ 'artemisApp.codeOfConduct.title' | artemisTranslate }}

-
+
+ +
@@ -9,8 +10,9 @@

{{ 'artemisApp.codeOfConduct.title' | artemisTranslate }}

-

{{ 'artemisApp.codeOfConduct.title' | artemisTranslate }}

-
+
+ +
diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts index c988788843d7..fc83a7f3a396 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts @@ -31,6 +31,7 @@ import { OneToOneChatCreateDialogComponent } from './dialogs/one-to-one-chat-cre import { GroupChatCreateDialogComponent } from './dialogs/group-chat-create-dialog/group-chat-create-dialog.component'; import { GroupChatIconComponent } from './other/group-chat-icon/group-chat-icon.component'; import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; +import { CourseConversationsCodeOfConductComponent } from 'app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component'; const routes: Routes = [ { @@ -56,6 +57,7 @@ const routes: Routes = [ ], declarations: [ CourseConversationsComponent, + CourseConversationsCodeOfConductComponent, ConversationSelectionSidebarComponent, ConversationThreadSidebarComponent, ConversationMessagesComponent, From 8271a4b13234cd1cc053db05139fa959c6c64b56 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 13 Sep 2023 14:25:03 +0200 Subject: [PATCH 27/76] On review --- ...java => CourseCodeOfConductAgreement.java} | 2 +- ...urseCodeOfConductAgreementRepository.java} | 8 +-- .../CourseCodeOfConductAgreementService.java | 60 +++++++++++++++++++ .../service/CourseCodeOfConductService.java | 59 ------------------ .../www1/artemis/web/rest/FileResource.java | 2 +- .../conversation/ConversationResource.java | 22 +++---- .../changelog/20230908050001_changelog.xml | 4 +- .../templates/codeofconduct/README.md | 2 +- ...nversations-code-of-conduct.component.html | 2 +- ...conversations-code-of-conduct.component.ts | 7 --- .../course-conversations.component.ts | 8 --- .../conversations/conversation.service.ts | 4 +- ...fConduct.json => courseCodeOfConduct.json} | 0 ...fConduct.json => courseCodeOfConduct.json} | 0 ...urseCodeOfConductAgreementServiceTest.java | 51 ++++++++++++++++ .../CourseCodeOfConductServiceTest.java | 51 ---------------- 16 files changed, 134 insertions(+), 148 deletions(-) rename src/main/java/de/tum/in/www1/artemis/domain/{CourseCodeOfConduct.java => CourseCodeOfConductAgreement.java} (96%) rename src/main/java/de/tum/in/www1/artemis/repository/{CourseCodeOfConductRepository.java => CourseCodeOfConductAgreementRepository.java} (73%) create mode 100644 src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java delete mode 100644 src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductService.java rename src/main/webapp/i18n/de/{courseOfConduct.json => courseCodeOfConduct.json} (100%) rename src/main/webapp/i18n/en/{courseOfConduct.json => courseCodeOfConduct.json} (100%) create mode 100644 src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementServiceTest.java delete mode 100644 src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductServiceTest.java diff --git a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConduct.java b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java similarity index 96% rename from src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConduct.java rename to src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java index 4b8527aa49d8..e74349cbdb5c 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConduct.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java @@ -13,7 +13,7 @@ * A user's agreement of a course's code of conduct. */ @Entity -@Table(name = "course_code_of_conduct") +@Table(name = "course_code_of_conduct_agreement") @JsonInclude(JsonInclude.Include.NON_EMPTY) public class CourseCodeOfConduct extends DomainObject { diff --git a/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java similarity index 73% rename from src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductRepository.java rename to src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java index 44833317c26d..9e4865b95625 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java @@ -7,13 +7,13 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import de.tum.in.www1.artemis.domain.CourseCodeOfConduct; +import de.tum.in.www1.artemis.domain.CourseCodeOfConductAgreement; /** * Spring Data repository for the Code of Conduct entity. */ @Repository -public interface CourseCodeOfConductRepository extends JpaRepository { +public interface CourseCodeOfConductRepository extends JpaRepository { /** * Find the user's agreement to a course's code of conduct. @@ -24,8 +24,8 @@ public interface CourseCodeOfConductRepository extends JpaRepository findByCourseIdAndUserId(@Param("courseId") Long courseId, @Param("userId") Long userId); + Optional findByCourseIdAndUserId(@Param("courseId") Long courseId, @Param("userId") Long userId); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java b/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java new file mode 100644 index 000000000000..d178075b3fb2 --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java @@ -0,0 +1,60 @@ +package de.tum.in.www1.artemis.service; + +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import de.tum.in.www1.artemis.domain.Course; +import de.tum.in.www1.artemis.domain.CourseCodeOfConductAgreement; +import de.tum.in.www1.artemis.domain.User; +import de.tum.in.www1.artemis.repository.CourseCodeOfConductAgreementRepository; + +/** + * Service Implementation for managing a user's agreement to a course's code of conduct. + */ +@Service +public class CourseCodeOfConductAgreementService { + + private final CourseCodeOfConductAgreementRepository courseCodeOfConductAgreementRepository; + + CourseCodeOfConductAgreementService(CourseCodeOfConductAgreementRepository courseCodeOfConductAgreementRepository) { + this.courseCodeOfConductAgreementRepository = courseCodeOfConductAgreementRepository; + } + + /** + * Fetches if a user agreed to a course's code of conduct. + * + * @param user the user in the course + * @param course the code of conduct's course + * @return if the user agreed to the course's code of conduct + */ + public boolean fetchUserAgreesToCodeOfConductInCourse(User user, Course course) { + Optional courseCodeOfConductAgreement = courseCodeOfConductAgreementRepository.findByCourseIdAndUserId(course.getId(), user.getId()); + if (courseCodeOfConductAgreement.isPresent()) { + return courseCodeOfConductAgreement.get().getIsAccepted(); + } + else { + CourseCodeOfConductAgreement courseCodeOfConductAgreementNew = new CourseCodeOfConductAgreement(); + courseCodeOfConductAgreementNew.setCourse(course); + courseCodeOfConductAgreementNew.setUser(user); + courseCodeOfConductAgreementNew.setIsAccepted(false); + courseCodeOfConductAgreementRepository.save(courseCodeOfConductAgreementNew); + return courseCodeOfConductAgreementNew.getIsAccepted(); + } + } + + /** + * A user agrees to a course's code of conduct. + * + * @param user the user in the course + * @param course the code of conduct's course + */ + public void setUserAgreesToCodeOfConductInCourse(User user, Course course) { + CourseCodeOfConductAgreement courseCodeOfConductAgreement = courseCodeOfConductAgreementRepository.findByCourseIdAndUserId(course.getId(), user.getId()) + .orElse(new CourseCodeOfConductAgreement()); + courseCodeOfConductAgreement.setCourse(course); + courseCodeOfConductAgreement.setUser(user); + courseCodeOfConductAgreement.setIsAccepted(true); + courseCodeOfConductAgreementRepository.save(courseCodeOfConductAgreement); + } +} diff --git a/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductService.java b/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductService.java deleted file mode 100644 index 52058c0133e8..000000000000 --- a/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductService.java +++ /dev/null @@ -1,59 +0,0 @@ -package de.tum.in.www1.artemis.service; - -import java.util.Optional; - -import org.springframework.stereotype.Service; - -import de.tum.in.www1.artemis.domain.Course; -import de.tum.in.www1.artemis.domain.CourseCodeOfConduct; -import de.tum.in.www1.artemis.domain.User; -import de.tum.in.www1.artemis.repository.CourseCodeOfConductRepository; - -/** - * Service Implementation for managing a course's code of conduct. - */ -@Service -public class CourseCodeOfConductService { - - private final CourseCodeOfConductRepository courseCodeOfConductRepository; - - CourseCodeOfConductService(CourseCodeOfConductRepository courseCodeOfConductRepository) { - this.courseCodeOfConductRepository = courseCodeOfConductRepository; - } - - /** - * Fetches if a user agreed to a course's code of conduct. - * - * @param user the user in the course - * @param course the code of conduct's course - * @return if the user agreed to the course's code of conduct - */ - public boolean fetchUserAgreesToCodeOfConductInCourse(User user, Course course) { - Optional courseCodeOfConduct = courseCodeOfConductRepository.findByCourseIdAndUserId(course.getId(), user.getId()); - if (courseCodeOfConduct.isPresent()) { - return courseCodeOfConduct.get().getIsCodeOfConductAccepted(); - } - else { - CourseCodeOfConduct courseCodeOfConductNew = new CourseCodeOfConduct(); - courseCodeOfConductNew.setCourse(course); - courseCodeOfConductNew.setUser(user); - courseCodeOfConductNew.setIsCodeOfConductAccepted(false); - courseCodeOfConductRepository.save(courseCodeOfConductNew); - return courseCodeOfConductNew.getIsCodeOfConductAccepted(); - } - } - - /** - * A user agrees to a course's code of conduct. - * - * @param user the user in the course - * @param course the code of conduct's course - */ - public void setUserAgreesToCodeOfConductInCourse(User user, Course course) { - CourseCodeOfConduct courseCodeOfConduct = courseCodeOfConductRepository.findByCourseIdAndUserId(course.getId(), user.getId()).orElse(new CourseCodeOfConduct()); - courseCodeOfConduct.setCourse(course); - courseCodeOfConduct.setUser(user); - courseCodeOfConduct.setIsCodeOfConductAccepted(true); - courseCodeOfConductRepository.save(courseCodeOfConduct); - } -} diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java index 4622c1e28308..18934cae4349 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java @@ -305,7 +305,7 @@ public ResponseEntity getCourseIcon(@PathVariable Long courseId) { } /** - * GET /files/course/code-of-conduct/template : Get the Code of Conduct template + * GET /files/templates/code-of-conduct : Get the Code of Conduct template * * @return The requested file, 403 if the logged-in user is not allowed to access it, or 404 if the file doesn't exist */ diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java index 39e7a68e303b..bfe452621e4b 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java @@ -22,7 +22,7 @@ import de.tum.in.www1.artemis.security.Role; import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastStudent; import de.tum.in.www1.artemis.service.AuthorizationCheckService; -import de.tum.in.www1.artemis.service.CourseCodeOfConductService; +import de.tum.in.www1.artemis.service.CourseCodeOfConductAgreementService; import de.tum.in.www1.artemis.service.dto.UserPublicInfoDTO; import de.tum.in.www1.artemis.service.metis.conversation.ConversationService; import de.tum.in.www1.artemis.service.metis.conversation.ConversationService.ConversationMemberSearchFilters; @@ -45,18 +45,18 @@ public class ConversationResource extends ConversationManagementResource { private final AuthorizationCheckService authorizationCheckService; - private final CourseCodeOfConductService courseCodeOfConductService; + private final CourseCodeOfConductAgreementService courseCodeOfConductAgreementService; private final UserRepository userRepository; public ConversationResource(ConversationService conversationService, ChannelAuthorizationService channelAuthorizationService, AuthorizationCheckService authorizationCheckService, UserRepository userRepository, CourseRepository courseRepository, - CourseCodeOfConductService courseCodeOfConductService) { + CourseCodeOfConductAgreementService courseCodeOfConductAgreementService) { super(courseRepository); this.conversationService = conversationService; this.channelAuthorizationService = channelAuthorizationService; this.authorizationCheckService = authorizationCheckService; - this.courseCodeOfConductService = courseCodeOfConductService; + this.courseCodeOfConductAgreementService = courseCodeOfConductAgreementService; this.userRepository = userRepository; } @@ -130,36 +130,36 @@ public ResponseEntity hasUnreadMessages(@PathVariable Long courseId) { } /** - * GET /api/courses/:courseId/code-of-conduct : Checks if the user agrees to the code of conduct + * GET /api/courses/:courseId/code-of-conduct-agreement : Checks if the user agrees to the code of conduct * * @param courseId the course's ID * @return ResponseEntity with status 200 (Ok) and body is true if the user agreed to the course's code of conduct */ - @GetMapping("/{courseId}/code-of-conduct") + @GetMapping("/{courseId}/code-of-conduct-agreement") @EnforceAtLeastStudent public ResponseEntity isCodeOfConductAccepted(@PathVariable Long courseId) { checkMessagingEnabledElseThrow(courseId); var course = courseRepository.findByIdElseThrow(courseId); var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, courseRepository.findByIdElseThrow(courseId), requestingUser); - return ResponseEntity.ok(courseCodeOfConductService.fetchUserAgreesToCodeOfConductInCourse(requestingUser, course)); + return ResponseEntity.ok(courseCodeOfConductAgreementService.fetchUserAgreesToCodeOfConductInCourse(requestingUser, course)); } /** - * POST /api/courses/:courseId/code-of-conduct : Accept the course's code of conduct + * POST /api/courses/:courseId/code-of-conduct-agreement : Accept the course's code of conduct * * @param courseId the course's ID * @return ResponseEntity with status 200 (Ok) and body is true if the user agreed to the code of conduct */ - @PostMapping("/{courseId}/code-of-conduct") + @PostMapping("/{courseId}/code-of-conduct-agreement") @EnforceAtLeastStudent public ResponseEntity acceptCodeOfConduct(@PathVariable Long courseId) { checkMessagingEnabledElseThrow(courseId); var course = courseRepository.findByIdElseThrow(courseId); var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, courseRepository.findByIdElseThrow(courseId), requestingUser); - courseCodeOfConductService.setUserAgreesToCodeOfConductInCourse(requestingUser, course); - return ResponseEntity.ok(courseCodeOfConductService.fetchUserAgreesToCodeOfConductInCourse(requestingUser, course)); + courseCodeOfConductAgreementService.setUserAgreesToCodeOfConductInCourse(requestingUser, course); + return ResponseEntity.ok(courseCodeOfConductAgreementService.fetchUserAgreesToCodeOfConductInCourse(requestingUser, course)); } /** diff --git a/src/main/resources/config/liquibase/changelog/20230908050001_changelog.xml b/src/main/resources/config/liquibase/changelog/20230908050001_changelog.xml index a88b7bce5fa7..ad0ef4c6f239 100644 --- a/src/main/resources/config/liquibase/changelog/20230908050001_changelog.xml +++ b/src/main/resources/config/liquibase/changelog/20230908050001_changelog.xml @@ -3,13 +3,13 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"> - + - + diff --git a/src/main/resources/templates/codeofconduct/README.md b/src/main/resources/templates/codeofconduct/README.md index be6379e3489e..2b85beb83449 100644 --- a/src/main/resources/templates/codeofconduct/README.md +++ b/src/main/resources/templates/codeofconduct/README.md @@ -1,4 +1,4 @@ -# Template: Adapt to your demands +# Code of Conduct Template: Adapt to your demands We as students, tutors, and instructors pledge to make participation in our course a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. diff --git a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html index bda05813c9e2..fd3b7bc97c35 100644 --- a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html +++ b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html @@ -1,5 +1,5 @@

{{ 'artemisApp.codeOfConduct.title' | artemisTranslate }}

-
+
  • {{ getUserLabel(user) }} diff --git a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts index 52b82b81b69f..783bf08994fa 100644 --- a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts +++ b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts @@ -14,13 +14,6 @@ export class CourseConversationsCodeOfConductComponent implements OnInit { @Input() course: Course; - get codeOfConduct(): string { - if (this.course.courseInformationSharingMessagingCodeOfConduct) { - return this.course.courseInformationSharingMessagingCodeOfConduct; - } - return ''; - } - responsibleContacts: UserPublicInfoDTO[] = []; constructor( diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts index a637073854b8..b1846088f4c8 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts @@ -28,14 +28,6 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { isCodeOfConductAccepted: boolean = false; isCodeOfConductPresented: boolean = false; - get codeOfConduct(): string { - if (this.course?.courseInformationSharingMessagingCodeOfConduct) { - return this.course?.courseInformationSharingMessagingCodeOfConduct; - } else { - return ''; - } - } - // MetisConversationService is created in course overview, so we can use it here constructor( private router: Router, diff --git a/src/main/webapp/app/shared/metis/conversations/conversation.service.ts b/src/main/webapp/app/shared/metis/conversations/conversation.service.ts index c352a4610234..34bf237a7e7f 100644 --- a/src/main/webapp/app/shared/metis/conversations/conversation.service.ts +++ b/src/main/webapp/app/shared/metis/conversations/conversation.service.ts @@ -123,11 +123,11 @@ export class ConversationService { } acceptCodeOfConduct(courseId: number): Observable> { - return this.http.post(`${this.resourceUrl}${courseId}/code-of-conduct`, null, { observe: 'response' }); + return this.http.post(`${this.resourceUrl}${courseId}/code-of-conduct-agreement`, null, { observe: 'response' }); } checkIfCodeOfConductIsAccepted(courseId: number): Observable> { - return this.http.get(`${this.resourceUrl}${courseId}/code-of-conduct`, { observe: 'response' }); + return this.http.get(`${this.resourceUrl}${courseId}/code-of-conduct-agreement`, { observe: 'response' }); } public convertDateFromClient = (conversation: Conversation) => ({ diff --git a/src/main/webapp/i18n/de/courseOfConduct.json b/src/main/webapp/i18n/de/courseCodeOfConduct.json similarity index 100% rename from src/main/webapp/i18n/de/courseOfConduct.json rename to src/main/webapp/i18n/de/courseCodeOfConduct.json diff --git a/src/main/webapp/i18n/en/courseOfConduct.json b/src/main/webapp/i18n/en/courseCodeOfConduct.json similarity index 100% rename from src/main/webapp/i18n/en/courseOfConduct.json rename to src/main/webapp/i18n/en/courseCodeOfConduct.json diff --git a/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementServiceTest.java b/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementServiceTest.java new file mode 100644 index 000000000000..f81674d894d9 --- /dev/null +++ b/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementServiceTest.java @@ -0,0 +1,51 @@ +package de.tum.in.www1.artemis.service; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.ZonedDateTime; +import java.util.HashSet; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import de.tum.in.www1.artemis.AbstractSpringIntegrationBambooBitbucketJiraTest; +import de.tum.in.www1.artemis.course.CourseFactory; +import de.tum.in.www1.artemis.repository.CourseRepository; +import de.tum.in.www1.artemis.user.UserUtilService; + +class CourseCodeOfConductAgreementServiceTest extends AbstractSpringIntegrationBambooBitbucketJiraTest { + + private static final String TEST_PREFIX = "coursecodeofconductservice"; + + private static final ZonedDateTime PAST_TIMESTAMP = ZonedDateTime.now().minusDays(1); + + private static final ZonedDateTime FUTURE_TIMESTAMP = ZonedDateTime.now().plusDays(1); + + @Autowired + private CourseRepository courseRepository; + + @Autowired + private CourseCodeOfConductAgreementService courseCodeOfConductAgreementService; + + @Autowired + private UserUtilService userUtilService; + + @BeforeEach + void init() { + userUtilService.addUsers(TEST_PREFIX, 1, 0, 0, 3); + } + + @Test + void fetchAndAgreeIsCodeOfConductAccepted() { + var user = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); + var course = CourseFactory.generateCourse(null, PAST_TIMESTAMP, FUTURE_TIMESTAMP, new HashSet<>(), "student", "tutor", "editor", "instructor"); + courseRepository.save(course); + var resultBeforeAgreement = courseCodeOfConductAgreementService.fetchUserAgreesToCodeOfConductInCourse(user, course); + assertThat(resultBeforeAgreement).isFalse(); + + courseCodeOfConductAgreementService.setUserAgreesToCodeOfConductInCourse(user, course); + var resultAfterAgreement = courseCodeOfConductAgreementService.fetchUserAgreesToCodeOfConductInCourse(user, course); + assertThat(resultAfterAgreement).isTrue(); + } +} diff --git a/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductServiceTest.java b/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductServiceTest.java deleted file mode 100644 index 952bbd1ff8fe..000000000000 --- a/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductServiceTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package de.tum.in.www1.artemis.service; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.ZonedDateTime; -import java.util.HashSet; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import de.tum.in.www1.artemis.AbstractSpringIntegrationBambooBitbucketJiraTest; -import de.tum.in.www1.artemis.course.CourseFactory; -import de.tum.in.www1.artemis.repository.CourseRepository; -import de.tum.in.www1.artemis.user.UserUtilService; - -class CourseCodeOfConductServiceTest extends AbstractSpringIntegrationBambooBitbucketJiraTest { - - private static final String TEST_PREFIX = "coursecodeofconductservice"; - - private static final ZonedDateTime PastTimestamp = ZonedDateTime.now().minusDays(1); - - private static final ZonedDateTime FutureTimestamp = ZonedDateTime.now().plusDays(1); - - @Autowired - private CourseRepository courseRepo; - - @Autowired - private CourseCodeOfConductService courseCodeOfConductService; - - @Autowired - private UserUtilService userUtilService; - - @BeforeEach - void init() { - userUtilService.addUsers(TEST_PREFIX, 1, 0, 0, 0); - } - - @Test - void fetchAndAgreeIsCodeOfConductAccepted() { - var user = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); - var course = CourseFactory.generateCourse(null, PastTimestamp, FutureTimestamp, new HashSet<>(), "student", "tutor", "editor", "instructor"); - courseRepo.save(course); - var resultBeforeAgreement = courseCodeOfConductService.fetchUserAgreesToCodeOfConductInCourse(user, course); - assertThat(resultBeforeAgreement).isFalse(); - - courseCodeOfConductService.setUserAgreesToCodeOfConductInCourse(user, course); - var resultAfterAgreement = courseCodeOfConductService.fetchUserAgreesToCodeOfConductInCourse(user, course); - assertThat(resultAfterAgreement).isTrue(); - } -} From 0abdef7e12892736ff4cb45fd940b321b3b80925 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 13 Sep 2023 14:38:19 +0200 Subject: [PATCH 28/76] Update test --- src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java index ff7109d7ced6..edcb16616e71 100644 --- a/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java @@ -125,7 +125,7 @@ void testGetCourseIcon() throws Exception { @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void testGetCourseCodeOfConductTemplate() throws Exception { var template = request.get("/api/files/templates/code-of-conduct", HttpStatus.OK, String.class); - assertThat(template).startsWith("# Template"); + assertThat(template).startsWith("# Code of Conduct Template"); } @Test From fd702fa30210c25e0c9787917af747e7d4ae4182 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 13 Sep 2023 15:25:20 +0200 Subject: [PATCH 29/76] Fix test CourseConversationComponent by mockign component and observable --- .../course-conversations.component.spec.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts index 9d4bc9efe8c9..8cabcf8c66e8 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts @@ -20,6 +20,7 @@ import { MetisService } from 'app/shared/metis/metis.service'; import { Post } from 'app/entities/metis/post.model'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; +import { CourseConversationsCodeOfConductComponent } from 'app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component'; const examples: (ConversationDto | undefined)[] = [undefined, generateOneToOneChatDTO({}), generateExampleGroupChatDTO({}), generateExampleChannelDTO({})]; @@ -46,6 +47,7 @@ examples.forEach((activeConversation) => { MockComponent(ConversationHeaderComponent), MockComponent(ConversationMessagesComponent), MockComponent(ConversationThreadSidebarComponent), + MockComponent(CourseConversationsCodeOfConductComponent), MockPipe(ArtemisTranslatePipe), MockPipe(HtmlForMarkdownPipe), ], @@ -101,6 +103,12 @@ examples.forEach((activeConversation) => { Object.defineProperty(metisConversationService, 'isLoading$', { get: () => new BehaviorSubject(false).asObservable(), }); + Object.defineProperty(metisConversationService, 'isCodeOfConductAccepted$', { + get: () => new BehaviorSubject(true).asObservable(), + }); + Object.defineProperty(metisConversationService, 'isCodeOfConductPresented$', { + get: () => new BehaviorSubject(false).asObservable(), + }); Object.defineProperty(metisService, 'posts', { get: () => postsSubject.asObservable(), }); From fdb08c3664d507202491608bee802d0e4d17afa0 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 13 Sep 2023 16:10:32 +0200 Subject: [PATCH 30/76] Supplement and add id acceptCodeOfConductButton --- .../domain/CourseCodeOfConductAgreement.java | 14 +++++++------- .../CourseCodeOfConductAgreementRepository.java | 4 ++-- .../course-conversations.component.html | 4 +++- .../pageobjects/course/CourseCommunication.ts | 2 +- .../support/pageobjects/course/CourseMessages.ts | 6 +++++- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java index e74349cbdb5c..0f67d7ca7d65 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java @@ -15,7 +15,7 @@ @Entity @Table(name = "course_code_of_conduct_agreement") @JsonInclude(JsonInclude.Include.NON_EMPTY) -public class CourseCodeOfConduct extends DomainObject { +public class CourseCodeOfConductAgreement extends DomainObject { @ManyToOne @JsonIncludeProperties({ "id" }) @@ -27,8 +27,8 @@ public class CourseCodeOfConduct extends DomainObject { @NotNull private User user; - @Column(name = "is_code_of_conduct_accepted") - private Boolean isCodeOfConductAccepted; + @Column(name = "is_accepted") + private Boolean isAccepted; public Course getCourse() { return course; @@ -46,11 +46,11 @@ public void setUser(User user) { this.user = user; } - public Boolean getIsCodeOfConductAccepted() { - return isCodeOfConductAccepted; + public Boolean getIsAccepted() { + return isAccepted; } - public void setIsCodeOfConductAccepted(Boolean isCodeOfConductAccepted) { - this.isCodeOfConductAccepted = isCodeOfConductAccepted; + public void setIsAccepted(Boolean isAccepted) { + this.isAccepted = isAccepted; } } diff --git a/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java index 9e4865b95625..27a8c55af256 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java @@ -10,10 +10,10 @@ import de.tum.in.www1.artemis.domain.CourseCodeOfConductAgreement; /** - * Spring Data repository for the Code of Conduct entity. + * Spring Data repository for the Code of Conduct Agreement entity. */ @Repository -public interface CourseCodeOfConductRepository extends JpaRepository { +public interface CourseCodeOfConductAgreementRepository extends JpaRepository { /** * Find the user's agreement to a course's code of conduct. diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.html b/src/main/webapp/app/overview/course-conversations/course-conversations.component.html index f3c5dece5f14..263107ce5781 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.html +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.html @@ -3,7 +3,9 @@
    - +
diff --git a/src/test/cypress/support/pageobjects/course/CourseCommunication.ts b/src/test/cypress/support/pageobjects/course/CourseCommunication.ts index cec35cf7fd45..ae7b46fe2fc5 100644 --- a/src/test/cypress/support/pageobjects/course/CourseCommunication.ts +++ b/src/test/cypress/support/pageobjects/course/CourseCommunication.ts @@ -2,7 +2,7 @@ import { BASE_API, CourseWideContext, POST, PUT } from '../../constants'; import { titleCaseWord } from '../../utils'; /** - * A class which encapsulates UI selectors and actions for the course creation page. + * A class which encapsulates UI selectors and actions for the course communication page. */ export class CourseCommunicationPage { newPost() { diff --git a/src/test/cypress/support/pageobjects/course/CourseMessages.ts b/src/test/cypress/support/pageobjects/course/CourseMessages.ts index df690bbd5638..d83fa487f11b 100644 --- a/src/test/cypress/support/pageobjects/course/CourseMessages.ts +++ b/src/test/cypress/support/pageobjects/course/CourseMessages.ts @@ -1,7 +1,7 @@ import { BASE_API, DELETE, POST, PUT } from '../../constants'; /** - * A class which encapsulates UI selectors and actions for the course creation page. + * A class which encapsulates UI selectors and actions for the course messages page. */ export class CourseMessagesPage { createChannelButton() { @@ -213,4 +213,8 @@ export class CourseMessagesPage { cy.get('.conversation-list').should('not.contain.text', name); } } + + acceptCodeOfConductButton() { + cy.get('#acceptCodeOfConductButton').click(); + } } From 14cf321e5d970c34b4487c532d7a3da5f91aa8e5 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 13 Sep 2023 23:41:26 +0200 Subject: [PATCH 31/76] Try fixing E2E by accepting code of conduct --- src/test/cypress/e2e/course/CourseMessages.cy.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/cypress/e2e/course/CourseMessages.cy.ts b/src/test/cypress/e2e/course/CourseMessages.cy.ts index b08f6f670181..30f7c5f5e5cd 100644 --- a/src/test/cypress/e2e/course/CourseMessages.cy.ts +++ b/src/test/cypress/e2e/course/CourseMessages.cy.ts @@ -24,6 +24,7 @@ describe('Course messages', () => { describe('Create channel', () => { it('check for pre-created channels', () => { cy.login(instructor, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); courseMessages.browseChannelsButton(); courseMessages.checkChannelsExists('tech-support'); courseMessages.checkChannelsExists('organization'); From e225a7f23a8bb3a83541574c1c2c974bbc1c1e1e Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 14 Sep 2023 00:47:39 +0200 Subject: [PATCH 32/76] Try fixing E2E by accepting code of conduct --- src/test/cypress/e2e/course/CourseMessages.cy.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/cypress/e2e/course/CourseMessages.cy.ts b/src/test/cypress/e2e/course/CourseMessages.cy.ts index 30f7c5f5e5cd..b79737596195 100644 --- a/src/test/cypress/e2e/course/CourseMessages.cy.ts +++ b/src/test/cypress/e2e/course/CourseMessages.cy.ts @@ -34,6 +34,7 @@ describe('Course messages', () => { it('instructors should be able to create public announcement channel', () => { cy.login(instructor, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); const name = 'public-ancmnt-ch'; courseMessages.createChannelButton(); courseMessages.setName(name); From bac667029a5d0522d1259f8307e5db0fe915ca64 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 14 Sep 2023 01:17:31 +0200 Subject: [PATCH 33/76] On Messages, check if code of conduct is accepted --- .../course-conversations/course-conversations.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts index b1846088f4c8..10c2c93b54bb 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts @@ -66,6 +66,7 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { this.subscribeToLoading(); this.isServiceSetUp = true; this.updateQueryParameters(); + this.metisConversationService.checkIsCodeOfConductAccepted(this.course!); } }); } From 2d60af2e3f102f93aab365541d1e2d916fe7660b Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 14 Sep 2023 18:00:09 +0200 Subject: [PATCH 34/76] Course Conversations Code Of Conduct Component --- ...rsations-code-of-conduct.component.spec.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts diff --git a/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts new file mode 100644 index 000000000000..9e0595657e76 --- /dev/null +++ b/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts @@ -0,0 +1,42 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockPipe, MockProvider } from 'ng-mocks'; +import { CourseConversationsCodeOfConductComponent } from 'app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component'; +import { ArtemisTestModule } from '../../../test.module'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; +import { CourseManagementService } from 'app/course/manage/course-management.service'; +import { AlertService } from 'app/core/util/alert.service'; + +describe('Course Conversations Code Of Conduct Component', () => { + let fixture: ComponentFixture; + let component: CourseConversationsCodeOfConductComponent; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ArtemisTestModule], + declarations: [ + CourseConversationsCodeOfConductComponent, + MockPipe(ArtemisTranslatePipe), + MockPipe(HtmlForMarkdownPipe), + MockProvider(AlertService), + MockProvider(CourseManagementService), + ], + providers: [], + }) + .compileComponents() + .then(() => { + fixture = TestBed.createComponent(CourseConversationsCodeOfConductComponent); + component = fixture.componentInstance; + component.course = { id: 1 }; + }); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should initialize', () => { + fixture.detectChanges(); + expect(component).not.toBeNull(); + }); +}); From 7c8712e947cd8bc8cfa292f488ad5f6a6983a9ec Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 14 Sep 2023 23:58:29 +0200 Subject: [PATCH 35/76] Fix TypeError: this.metisConversationService.checkIsCodeOfConductAccepted is not a function --- .../course-conversations.component.spec.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts index 8cabcf8c66e8..a82f94c4e878 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts @@ -109,6 +109,11 @@ examples.forEach((activeConversation) => { Object.defineProperty(metisConversationService, 'isCodeOfConductPresented$', { get: () => new BehaviorSubject(false).asObservable(), }); + Object.defineProperty(metisConversationService, 'checkIsCodeOfConductAccepted', { + get: () => { + return () => {}; + }, + }); Object.defineProperty(metisService, 'posts', { get: () => postsSubject.asObservable(), }); From 77d8a5d2c152d0e83147884cd6e6ca9104563f7c Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Fri, 15 Sep 2023 00:07:16 +0200 Subject: [PATCH 36/76] Should update course information sharing code of conduct --- .../component/course/course-update.component.spec.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/test/javascript/spec/component/course/course-update.component.spec.ts b/src/test/javascript/spec/component/course/course-update.component.spec.ts index cee5192b8d4b..536ccf0c92b5 100644 --- a/src/test/javascript/spec/component/course/course-update.component.spec.ts +++ b/src/test/javascript/spec/component/course/course-update.component.spec.ts @@ -356,6 +356,17 @@ describe('Course Management Update Component', () => { }); }); + describe('updateCourseInformationSharingMessagingCodeOfConduct', () => { + it('should update course information sharing code of conduct', () => { + comp.course = new Course(); + comp.courseForm = new FormGroup({ + courseInformationSharingMessagingCodeOfConduct: new FormControl(), + }); + comp.updateCourseInformationSharingMessagingCodeOfConduct('# Code of Conduct'); + expect(comp.courseForm.controls['courseInformationSharingMessagingCodeOfConduct'].value).toBe('# Code of Conduct'); + }); + }); + describe('changeComplaintsEnabled', () => { it('should initialize values if enabled and reset if disabled', () => { comp.courseForm = new FormGroup({ From 3944ee8afe7d35e45cbe32be56eb80adbdb46526 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Fri, 15 Sep 2023 00:09:56 +0200 Subject: [PATCH 37/76] Fix TypeError: Cannot read properties of undefined (reading '__ngMocksConfig') --- ...e-conversations-code-of-conduct.component.spec.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts index 9e0595657e76..82d88e6323d7 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts @@ -1,11 +1,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MockPipe, MockProvider } from 'ng-mocks'; +import { MockPipe } from 'ng-mocks'; import { CourseConversationsCodeOfConductComponent } from 'app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component'; import { ArtemisTestModule } from '../../../test.module'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; -import { CourseManagementService } from 'app/course/manage/course-management.service'; -import { AlertService } from 'app/core/util/alert.service'; describe('Course Conversations Code Of Conduct Component', () => { let fixture: ComponentFixture; @@ -14,13 +12,7 @@ describe('Course Conversations Code Of Conduct Component', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [ArtemisTestModule], - declarations: [ - CourseConversationsCodeOfConductComponent, - MockPipe(ArtemisTranslatePipe), - MockPipe(HtmlForMarkdownPipe), - MockProvider(AlertService), - MockProvider(CourseManagementService), - ], + declarations: [CourseConversationsCodeOfConductComponent, MockPipe(ArtemisTranslatePipe), MockPipe(HtmlForMarkdownPipe)], providers: [], }) .compileComponents() From fd4a97db514eb6352b6d1bf291c4e3c7205c85ca Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Fri, 15 Sep 2023 01:12:29 +0200 Subject: [PATCH 38/76] Test should display responsible contacts --- ...rsations-code-of-conduct.component.spec.ts | 32 ++++++++++++++++--- .../service/mock-course-management.service.ts | 7 +++- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts index 82d88e6323d7..86c27af62970 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts @@ -1,25 +1,40 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MockPipe } from 'ng-mocks'; +import { MockPipe, MockProvider } from 'ng-mocks'; import { CourseConversationsCodeOfConductComponent } from 'app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component'; import { ArtemisTestModule } from '../../../test.module'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; +import { CourseManagementService } from 'app/course/manage/course-management.service'; +import { AlertService } from 'app/core/util/alert.service'; +import { MockCourseManagementService } from '../../../helpers/mocks/service/mock-course-management.service'; +import { UserPublicInfoDTO } from 'app/core/user/user.model'; +import { Observable, of } from 'rxjs'; +import { HttpResponse } from '@angular/common/http'; describe('Course Conversations Code Of Conduct Component', () => { - let fixture: ComponentFixture; let component: CourseConversationsCodeOfConductComponent; + let fixture: ComponentFixture; + + let courseManagementService: CourseManagementService; + let courseManagementServiceStub: jest.SpyInstance; beforeEach(() => { TestBed.configureTestingModule({ imports: [ArtemisTestModule], declarations: [CourseConversationsCodeOfConductComponent, MockPipe(ArtemisTranslatePipe), MockPipe(HtmlForMarkdownPipe)], - providers: [], + providers: [MockProvider(AlertService), { provide: CourseManagementService, useClass: MockCourseManagementService }], }) .compileComponents() .then(() => { fixture = TestBed.createComponent(CourseConversationsCodeOfConductComponent); component = fixture.componentInstance; + component.course = { id: 1 }; + + courseManagementService = TestBed.inject(CourseManagementService); + courseManagementServiceStub = jest + .spyOn(courseManagementService, 'searchUsers') + .mockReturnValue(of({ body: [{ firstName: '', lastName: '', email: '' }] }) as Observable>); }); }); @@ -29,6 +44,15 @@ describe('Course Conversations Code Of Conduct Component', () => { it('should initialize', () => { fixture.detectChanges(); - expect(component).not.toBeNull(); + expect(CourseConversationsCodeOfConductComponent).not.toBeNull(); + }); + + it('should display responsible contacts', () => { + const getUserLabelSpy = jest.spyOn(component, 'getUserLabel'); + + fixture.detectChanges(); + + expect(getUserLabelSpy).toHaveBeenCalledTimes(2); + expect(courseManagementServiceStub).toHaveBeenCalledOnce(); }); }); diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-course-management.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-course-management.service.ts index ee9a21194fd3..a7f4b5efd75a 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-course-management.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-course-management.service.ts @@ -4,7 +4,8 @@ import { Course, CourseGroup } from 'app/entities/course.model'; import { TextExercise } from 'app/entities/text-exercise.model'; import { Exercise } from 'app/entities/exercise.model'; import { User } from '@sentry/angular-ivy'; -import { EntityArrayResponseType } from 'app/course/manage/course-management.service'; +import { EntityArrayResponseType, RoleGroup } from 'app/course/manage/course-management.service'; +import { UserPublicInfoDTO } from 'app/core/user/user.model'; export class MockCourseManagementService { mockExercises: Exercise[] = [new TextExercise(undefined, undefined)]; @@ -41,4 +42,8 @@ export class MockCourseManagementService { getAll(): Observable { return of(new HttpResponse({ body: [] })); } + + searchUsers(courseId: number, loginOrName: string, roles: RoleGroup[]): Observable> { + return of(new HttpResponse({ body: [] })); + } } From 01f78d0ae04f0e1250c32653611a41338fa4055c Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Fri, 15 Sep 2023 01:57:15 +0200 Subject: [PATCH 39/76] Integration test code of conduct --- .../metis/ConversationIntegrationTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java index 25cc99879c51..0a8391767d80 100644 --- a/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java @@ -428,6 +428,21 @@ void unreadMessages_shouldReturnCorrectValue_Message() throws Exception { assertThat(unreadMessages).isTrue(); } + @Test + @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") + void codeOfConduct_isAccepted() throws Exception { + var agreement = request.get("/api/courses/" + exampleCourseId + "/code-of-conduct-agreement", HttpStatus.OK, Boolean.class); + assertThat(agreement).isFalse(); + } + + @Test + @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") + void codeOfConduct_accept() throws Exception { + var agreement = request.postWithResponseBody("/api/courses/" + exampleCourseId + "/code-of-conduct-agreement", HttpStatus.OK, Boolean.class); + assertThat(agreement).isTrue(); + + } + private void assertConversationDTOTransientProperties(ConversationDTO conversationDTO, Boolean isCreator, Boolean isMember, Boolean hasChannelModerationRights, Boolean isChannelModerator) { assertThat(conversationDTO.getIsCreator()).isEqualTo(isCreator); From d4931e8032758ae45e27d773decdd31a26b84e51 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Fri, 15 Sep 2023 02:15:37 +0200 Subject: [PATCH 40/76] Try fixing E2E by accepting code of conduct --- .../cypress/e2e/course/CourseMessages.cy.ts | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/test/cypress/e2e/course/CourseMessages.cy.ts b/src/test/cypress/e2e/course/CourseMessages.cy.ts index b79737596195..9cf65ffd9c10 100644 --- a/src/test/cypress/e2e/course/CourseMessages.cy.ts +++ b/src/test/cypress/e2e/course/CourseMessages.cy.ts @@ -47,6 +47,7 @@ describe('Course messages', () => { it('instructors should be able to create private announcement channel', () => { cy.login(instructor, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); const name = 'private-ancmnt-ch'; courseMessages.createChannelButton(); courseMessages.setName(name); @@ -59,6 +60,7 @@ describe('Course messages', () => { it('instructors should be able to create public unrestricted channel', () => { cy.login(instructor, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); const name = 'public-unrstct-ch'; courseMessages.createChannelButton(); courseMessages.setName(name); @@ -71,6 +73,7 @@ describe('Course messages', () => { it('instructors should be able to create private unrestricted channel', () => { cy.login(instructor, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); const name = 'private-unrstct-ch'; courseMessages.createChannelButton(); courseMessages.setName(name); @@ -83,6 +86,7 @@ describe('Course messages', () => { it('instructors should not be able to create channel with uppercase name', () => { cy.login(instructor, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); const name = 'Forbidden Name'; courseMessages.createChannelButton(); courseMessages.setName(name); @@ -91,6 +95,7 @@ describe('Course messages', () => { it('instructors should not be able to create channel with name longer than 30 chars', () => { cy.login(instructor, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); const name = 'way-way-way-too-long-channel-title'; courseMessages.createChannelButton(); courseMessages.setName(name); @@ -101,6 +106,7 @@ describe('Course messages', () => { cy.login(admin); courseManagementAPIRequest.createLecture(course, 'Test Lecture'); cy.login(instructor, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); courseMessages.browseLectureChannelsButton(); courseMessages.checkChannelsExists('lecture-test-lecture'); }); @@ -109,6 +115,7 @@ describe('Course messages', () => { cy.login(admin); exerciseAPIRequest.createTextExercise({ course }, 'Test Exercise'); cy.login(instructor, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); courseMessages.browseExerciseChannelsButton(); courseMessages.checkChannelsExists('exercise-test-exercise'); }); @@ -118,6 +125,7 @@ describe('Course messages', () => { const examTitle = 'exam' + generateUUID(); examAPIRequests.createExam({ course, title: examTitle }); cy.login(instructor, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); courseMessages.browseExamChannelsButton(); courseMessages.checkChannelsExists(titleLowercase(examTitle)); }); @@ -135,6 +143,7 @@ describe('Course messages', () => { it('instructors should be able to edit a channel', () => { cy.login(instructor, `/courses/${course.id}/messages?conversationId=${channel.id}`); + courseMessages.acceptCodeOfConductButton(); const newName = 'new-test-name'; const topic = 'test-topic'; courseMessages.getName().click(); @@ -159,6 +168,7 @@ describe('Course messages', () => { it('student should be joined into pre-created channels automatically', () => { cy.login(studentOne, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); courseMessages.browseChannelsButton(); courseMessages.getChannelIdByName('tech-support').then((response) => { const techSupportChannelId = Number(response!); @@ -180,6 +190,7 @@ describe('Course messages', () => { it('student should be able to join a public channel', () => { cy.login(studentOne, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); courseMessages.browseChannelsButton(); courseMessages.joinChannel(channel.id!); courseMessages.checkBadgeJoined(channel.id!).should('exist').contains('Joined'); @@ -187,6 +198,7 @@ describe('Course messages', () => { it('student should be able to leave a public channel', () => { cy.login(studentOne, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); courseMessages.browseChannelsButton(); courseMessages.leaveChannel(channel.id!); courseMessages.checkBadgeJoined(channel.id!).should('not.exist'); @@ -205,6 +217,7 @@ describe('Course messages', () => { it('student should be able to write message in channel', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${channel.id}`); + courseMessages.acceptCodeOfConductButton(); const messageText = 'Student Test Message'; courseMessages.writeMessage(messageText); courseMessages.save().then((interception) => { @@ -215,6 +228,7 @@ describe('Course messages', () => { it('student should be able to edit message in channel', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${channel.id}`); + courseMessages.acceptCodeOfConductButton(); const messageText = 'Student Edit Test Message'; communicationAPIRequest.createCourseMessage(course, channel.id!, 'channel', messageText).then((response) => { const message = response.body; @@ -225,8 +239,9 @@ describe('Course messages', () => { }); }); - it('student should be able to delete his message in channel', () => { + it('student should be able to delete message in channel', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${channel.id}`); + courseMessages.acceptCodeOfConductButton(); const messageText = 'Student Edit Test Message'; communicationAPIRequest.createCourseMessage(course, channel.id!, 'channel', messageText).then((response) => { const message = response.body; @@ -263,6 +278,7 @@ describe('Course messages', () => { describe('Create group chat', () => { it('instructors should be able to create group chat', () => { cy.login(instructor, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); courseMessages.createGroupChatButton(); courseMessages.addUserToGroupChat(studentOne.username); courseMessages.addUserToGroupChat(studentTwo.username); @@ -277,6 +293,7 @@ describe('Course messages', () => { it('tutor should be able to create group chat', () => { cy.login(tutor, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); courseMessages.createGroupChatButton(); courseMessages.addUserToGroupChat(studentOne.username); courseMessages.addUserToGroupChat(instructor.username); @@ -291,6 +308,7 @@ describe('Course messages', () => { it('student should be able to create group chat', () => { cy.login(studentOne, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); courseMessages.createGroupChatButton(); courseMessages.addUserToGroupChat(studentTwo.username); courseMessages.addUserToGroupChat(tutor.username); @@ -315,6 +333,7 @@ describe('Course messages', () => { it('tutor should be able to add user to group chat', () => { cy.login(tutor, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); + courseMessages.acceptCodeOfConductButton(); courseMessages.addUserToGroupChatButton(); courseMessages.addUserToGroupChat(instructor.username); courseMessages.updateGroupChat(); @@ -326,6 +345,7 @@ describe('Course messages', () => { it('student should be able to add user to group chat', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); + courseMessages.acceptCodeOfConductButton(); courseMessages.addUserToGroupChatButton(); courseMessages.addUserToGroupChat(studentTwo.username); courseMessages.updateGroupChat(); @@ -350,6 +370,7 @@ describe('Course messages', () => { it('tutor should be able to leave group chat', () => { cy.login(tutor, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); + courseMessages.acceptCodeOfConductButton(); courseMessages.checkGroupChatExists(groupChatName, true); courseMessages.listMembersButton(course.id!, groupChat.id!); courseMessages.openSettingsTab(); @@ -360,6 +381,7 @@ describe('Course messages', () => { it('student should be able to leave group chat', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); + courseMessages.acceptCodeOfConductButton(); courseMessages.checkGroupChatExists(groupChatName, true); courseMessages.listMembersButton(course.id!, groupChat.id!); courseMessages.openSettingsTab(); @@ -380,6 +402,7 @@ describe('Course messages', () => { it('student should be able to write message in group chat', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); + courseMessages.acceptCodeOfConductButton(); const messageText = 'Student Test Message'; courseMessages.writeMessage(messageText); courseMessages.save(true).then((interception) => { @@ -390,6 +413,7 @@ describe('Course messages', () => { it('student should be able to edit message in group chat', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); + courseMessages.acceptCodeOfConductButton(); const messageText = 'Student Edit Test Message'; communicationAPIRequest.createCourseMessage(course, groupChat.id!, 'groupChat', messageText).then((response) => { const message = response.body; @@ -400,8 +424,9 @@ describe('Course messages', () => { }); }); - it('student should be able to delete his message in group chat', () => { + it('student should be able to delete message in group chat', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); + courseMessages.acceptCodeOfConductButton(); const messageText = 'Student Edit Test Message'; communicationAPIRequest.createCourseMessage(course, groupChat.id!, 'groupChat', messageText).then((response) => { const message = response.body; From fa9193506b37e5083414a757a33e67307d1d4966 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Fri, 15 Sep 2023 02:52:38 +0200 Subject: [PATCH 41/76] Increase coverage --- .../course-conversations-code-of-conduct.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts index 86c27af62970..3493cc644b7d 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/course-conversations-code-of-conduct.component.spec.ts @@ -34,7 +34,7 @@ describe('Course Conversations Code Of Conduct Component', () => { courseManagementService = TestBed.inject(CourseManagementService); courseManagementServiceStub = jest .spyOn(courseManagementService, 'searchUsers') - .mockReturnValue(of({ body: [{ firstName: '', lastName: '', email: '' }] }) as Observable>); + .mockReturnValue(of({ body: [{ firstName: 'Alice', lastName: 'Appleseed', email: 'alice@example.com' }] }) as Observable>); }); }); From b8cc36bff76a5561eafc9fbfcaecc7e85b49490c Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Fri, 15 Sep 2023 02:59:15 +0200 Subject: [PATCH 42/76] Rename checkIfCodeOfConductIsAccepted --- .../app/shared/metis/conversations/conversation.service.ts | 2 +- src/main/webapp/app/shared/metis/metis-conversation.service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/app/shared/metis/conversations/conversation.service.ts b/src/main/webapp/app/shared/metis/conversations/conversation.service.ts index 34bf237a7e7f..9c52048d4824 100644 --- a/src/main/webapp/app/shared/metis/conversations/conversation.service.ts +++ b/src/main/webapp/app/shared/metis/conversations/conversation.service.ts @@ -126,7 +126,7 @@ export class ConversationService { return this.http.post(`${this.resourceUrl}${courseId}/code-of-conduct-agreement`, null, { observe: 'response' }); } - checkIfCodeOfConductIsAccepted(courseId: number): Observable> { + checkIsCodeOfConductAccepted(courseId: number): Observable> { return this.http.get(`${this.resourceUrl}${courseId}/code-of-conduct-agreement`, { observe: 'response' }); } 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 05c3cfdeb2b8..6269607f599d 100644 --- a/src/main/webapp/app/shared/metis/metis-conversation.service.ts +++ b/src/main/webapp/app/shared/metis/metis-conversation.service.ts @@ -279,7 +279,7 @@ export class MetisConversationService implements OnDestroy { return; } - this.conversationService.checkIfCodeOfConductIsAccepted(course.id).subscribe({ + this.conversationService.checkIsCodeOfConductAccepted(course.id).subscribe({ next: (response) => { if (response.body !== null) { this.isCodeOfConductAccepted = response.body; From bf6f1c65ea52728edef3b52fffdcbccdfc7a726e Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Tue, 19 Sep 2023 16:27:44 +0200 Subject: [PATCH 43/76] Revert "Try fixing E2E by accepting code of conduct" This reverts commit d4931e8032758ae45e27d773decdd31a26b84e51. --- .../cypress/e2e/course/CourseMessages.cy.ts | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/src/test/cypress/e2e/course/CourseMessages.cy.ts b/src/test/cypress/e2e/course/CourseMessages.cy.ts index 9cf65ffd9c10..6a11d22ba4d7 100644 --- a/src/test/cypress/e2e/course/CourseMessages.cy.ts +++ b/src/test/cypress/e2e/course/CourseMessages.cy.ts @@ -47,7 +47,6 @@ describe('Course messages', () => { it('instructors should be able to create private announcement channel', () => { cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); const name = 'private-ancmnt-ch'; courseMessages.createChannelButton(); courseMessages.setName(name); @@ -60,7 +59,6 @@ describe('Course messages', () => { it('instructors should be able to create public unrestricted channel', () => { cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); const name = 'public-unrstct-ch'; courseMessages.createChannelButton(); courseMessages.setName(name); @@ -73,7 +71,6 @@ describe('Course messages', () => { it('instructors should be able to create private unrestricted channel', () => { cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); const name = 'private-unrstct-ch'; courseMessages.createChannelButton(); courseMessages.setName(name); @@ -86,7 +83,6 @@ describe('Course messages', () => { it('instructors should not be able to create channel with uppercase name', () => { cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); const name = 'Forbidden Name'; courseMessages.createChannelButton(); courseMessages.setName(name); @@ -95,7 +91,6 @@ describe('Course messages', () => { it('instructors should not be able to create channel with name longer than 30 chars', () => { cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); const name = 'way-way-way-too-long-channel-title'; courseMessages.createChannelButton(); courseMessages.setName(name); @@ -106,7 +101,6 @@ describe('Course messages', () => { cy.login(admin); courseManagementAPIRequest.createLecture(course, 'Test Lecture'); cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); courseMessages.browseLectureChannelsButton(); courseMessages.checkChannelsExists('lecture-test-lecture'); }); @@ -115,7 +109,6 @@ describe('Course messages', () => { cy.login(admin); exerciseAPIRequest.createTextExercise({ course }, 'Test Exercise'); cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); courseMessages.browseExerciseChannelsButton(); courseMessages.checkChannelsExists('exercise-test-exercise'); }); @@ -125,7 +118,6 @@ describe('Course messages', () => { const examTitle = 'exam' + generateUUID(); examAPIRequests.createExam({ course, title: examTitle }); cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); courseMessages.browseExamChannelsButton(); courseMessages.checkChannelsExists(titleLowercase(examTitle)); }); @@ -143,7 +135,6 @@ describe('Course messages', () => { it('instructors should be able to edit a channel', () => { cy.login(instructor, `/courses/${course.id}/messages?conversationId=${channel.id}`); - courseMessages.acceptCodeOfConductButton(); const newName = 'new-test-name'; const topic = 'test-topic'; courseMessages.getName().click(); @@ -168,7 +159,6 @@ describe('Course messages', () => { it('student should be joined into pre-created channels automatically', () => { cy.login(studentOne, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); courseMessages.browseChannelsButton(); courseMessages.getChannelIdByName('tech-support').then((response) => { const techSupportChannelId = Number(response!); @@ -190,7 +180,6 @@ describe('Course messages', () => { it('student should be able to join a public channel', () => { cy.login(studentOne, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); courseMessages.browseChannelsButton(); courseMessages.joinChannel(channel.id!); courseMessages.checkBadgeJoined(channel.id!).should('exist').contains('Joined'); @@ -198,7 +187,6 @@ describe('Course messages', () => { it('student should be able to leave a public channel', () => { cy.login(studentOne, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); courseMessages.browseChannelsButton(); courseMessages.leaveChannel(channel.id!); courseMessages.checkBadgeJoined(channel.id!).should('not.exist'); @@ -217,7 +205,6 @@ describe('Course messages', () => { it('student should be able to write message in channel', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${channel.id}`); - courseMessages.acceptCodeOfConductButton(); const messageText = 'Student Test Message'; courseMessages.writeMessage(messageText); courseMessages.save().then((interception) => { @@ -228,7 +215,6 @@ describe('Course messages', () => { it('student should be able to edit message in channel', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${channel.id}`); - courseMessages.acceptCodeOfConductButton(); const messageText = 'Student Edit Test Message'; communicationAPIRequest.createCourseMessage(course, channel.id!, 'channel', messageText).then((response) => { const message = response.body; @@ -241,7 +227,6 @@ describe('Course messages', () => { it('student should be able to delete message in channel', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${channel.id}`); - courseMessages.acceptCodeOfConductButton(); const messageText = 'Student Edit Test Message'; communicationAPIRequest.createCourseMessage(course, channel.id!, 'channel', messageText).then((response) => { const message = response.body; @@ -278,7 +263,6 @@ describe('Course messages', () => { describe('Create group chat', () => { it('instructors should be able to create group chat', () => { cy.login(instructor, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); courseMessages.createGroupChatButton(); courseMessages.addUserToGroupChat(studentOne.username); courseMessages.addUserToGroupChat(studentTwo.username); @@ -293,7 +277,6 @@ describe('Course messages', () => { it('tutor should be able to create group chat', () => { cy.login(tutor, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); courseMessages.createGroupChatButton(); courseMessages.addUserToGroupChat(studentOne.username); courseMessages.addUserToGroupChat(instructor.username); @@ -308,7 +291,6 @@ describe('Course messages', () => { it('student should be able to create group chat', () => { cy.login(studentOne, `/courses/${course.id}/messages`); - courseMessages.acceptCodeOfConductButton(); courseMessages.createGroupChatButton(); courseMessages.addUserToGroupChat(studentTwo.username); courseMessages.addUserToGroupChat(tutor.username); @@ -333,7 +315,6 @@ describe('Course messages', () => { it('tutor should be able to add user to group chat', () => { cy.login(tutor, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); - courseMessages.acceptCodeOfConductButton(); courseMessages.addUserToGroupChatButton(); courseMessages.addUserToGroupChat(instructor.username); courseMessages.updateGroupChat(); @@ -345,7 +326,6 @@ describe('Course messages', () => { it('student should be able to add user to group chat', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); - courseMessages.acceptCodeOfConductButton(); courseMessages.addUserToGroupChatButton(); courseMessages.addUserToGroupChat(studentTwo.username); courseMessages.updateGroupChat(); @@ -370,7 +350,6 @@ describe('Course messages', () => { it('tutor should be able to leave group chat', () => { cy.login(tutor, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); - courseMessages.acceptCodeOfConductButton(); courseMessages.checkGroupChatExists(groupChatName, true); courseMessages.listMembersButton(course.id!, groupChat.id!); courseMessages.openSettingsTab(); @@ -381,7 +360,6 @@ describe('Course messages', () => { it('student should be able to leave group chat', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); - courseMessages.acceptCodeOfConductButton(); courseMessages.checkGroupChatExists(groupChatName, true); courseMessages.listMembersButton(course.id!, groupChat.id!); courseMessages.openSettingsTab(); @@ -402,7 +380,6 @@ describe('Course messages', () => { it('student should be able to write message in group chat', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); - courseMessages.acceptCodeOfConductButton(); const messageText = 'Student Test Message'; courseMessages.writeMessage(messageText); courseMessages.save(true).then((interception) => { @@ -413,7 +390,6 @@ describe('Course messages', () => { it('student should be able to edit message in group chat', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); - courseMessages.acceptCodeOfConductButton(); const messageText = 'Student Edit Test Message'; communicationAPIRequest.createCourseMessage(course, groupChat.id!, 'groupChat', messageText).then((response) => { const message = response.body; @@ -426,7 +402,6 @@ describe('Course messages', () => { it('student should be able to delete message in group chat', () => { cy.login(studentOne, `/courses/${course.id}/messages?conversationId=${groupChat.id}`); - courseMessages.acceptCodeOfConductButton(); const messageText = 'Student Edit Test Message'; communicationAPIRequest.createCourseMessage(course, groupChat.id!, 'groupChat', messageText).then((response) => { const message = response.body; From 583aca215b4d166aa96258e7dfcf9d4e46a073c9 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Tue, 19 Sep 2023 16:32:13 +0200 Subject: [PATCH 44/76] E2E: Accept code of conduct --- src/test/cypress/e2e/course/CourseMessages.cy.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/test/cypress/e2e/course/CourseMessages.cy.ts b/src/test/cypress/e2e/course/CourseMessages.cy.ts index 6a11d22ba4d7..0f3027dd97be 100644 --- a/src/test/cypress/e2e/course/CourseMessages.cy.ts +++ b/src/test/cypress/e2e/course/CourseMessages.cy.ts @@ -20,6 +20,17 @@ describe('Course messages', () => { }); }); + it('accepts code of conduct', () => { + cy.login(instructor, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); + cy.login(studentOne, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); + cy.login(studentTwo, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); + cy.login(tutor, `/courses/${course.id}/messages`); + courseMessages.acceptCodeOfConductButton(); + }); + describe('Channel messages', () => { describe('Create channel', () => { it('check for pre-created channels', () => { From c1da199fdaf91b5cee8e27664664a154d4f46033 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Tue, 19 Sep 2023 16:56:17 +0200 Subject: [PATCH 45/76] Confirm accept agreement https://github.com/ls1intum/Artemis/pull/7154#discussion_r1327941434 --- .../in/www1/artemis/metis/ConversationIntegrationTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java index 0a8391767d80..4fe7ce58eed7 100644 --- a/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java @@ -441,6 +441,10 @@ void codeOfConduct_accept() throws Exception { var agreement = request.postWithResponseBody("/api/courses/" + exampleCourseId + "/code-of-conduct-agreement", HttpStatus.OK, Boolean.class); assertThat(agreement).isTrue(); + var newAgreement = request.get("/api/courses/" + exampleCourseId + "/code-of-conduct-agreement", HttpStatus.OK, Boolean.class); + assertThat(newAgreement).isTrue(); + ; + } private void assertConversationDTOTransientProperties(ConversationDTO conversationDTO, Boolean isCreator, Boolean isMember, Boolean hasChannelModerationRights, From 59b450f4a596ec5d8e9361b399de2d32a1d5ec48 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Tue, 19 Sep 2023 18:19:07 +0200 Subject: [PATCH 46/76] Rebuild CourseCodeOfConductAgreement table --- .../domain/CourseCodeOfConductAgreement.java | 48 +++++++++++++------ .../CourseCodeOfConductAgreementId.java | 17 +++++++ .../changelog/20230908050001_changelog.xml | 15 ------ .../resources/config/liquibase/master.xml | 1 - 4 files changed, 51 insertions(+), 30 deletions(-) create mode 100644 src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java delete mode 100644 src/main/resources/config/liquibase/changelog/20230908050001_changelog.xml diff --git a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java index 0f67d7ca7d65..304010212e6f 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java @@ -1,13 +1,10 @@ package de.tum.in.www1.artemis.domain; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import javax.validation.constraints.NotNull; +import java.util.Objects; + +import javax.persistence.*; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonIncludeProperties; /** * A user's agreement of a course's code of conduct. @@ -15,20 +12,21 @@ @Entity @Table(name = "course_code_of_conduct_agreement") @JsonInclude(JsonInclude.Include.NON_EMPTY) -public class CourseCodeOfConductAgreement extends DomainObject { +@IdClass(CourseCodeOfConductAgreementId.class) +public class CourseCodeOfConductAgreement { + @Id @ManyToOne - @JsonIncludeProperties({ "id" }) - @NotNull + @JoinColumn(name = "course_id") private Course course; + @Id @ManyToOne - @JsonIncludeProperties({ "id" }) - @NotNull + @JoinColumn(name = "user_id") private User user; @Column(name = "is_accepted") - private Boolean isAccepted; + private boolean isAccepted = false; public Course getCourse() { return course; @@ -46,11 +44,33 @@ public void setUser(User user) { this.user = user; } - public Boolean getIsAccepted() { + public boolean getIsAccepted() { return isAccepted; } - public void setIsAccepted(Boolean isAccepted) { + public void setIsAccepted(boolean isAccepted) { this.isAccepted = isAccepted; } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + if (!super.equals(o)) + return false; + CourseCodeOfConductAgreement that = (CourseCodeOfConductAgreement) o; + return course.equals(that.course) && user.equals(that.user) && isAccepted == that.isAccepted; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), course, user, isAccepted); + } + + @Override + public String toString() { + return "CourseCodeOfConductAgreement{" + "course=" + course + ", user=" + user + ", isAccepted=" + isAccepted + '}'; + } } diff --git a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java new file mode 100644 index 000000000000..defc96808beb --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java @@ -0,0 +1,17 @@ +package de.tum.in.www1.artemis.domain; + +import java.io.Serializable; + +/** + * The primary key for CourseCodeOfConductAgreement + * + * @param course the course's id + * @param user the user's id + */ +record CourseCodeOfConductAgreementId(Long course, Long user) implements Serializable { + + // Needed for JPA + CourseCodeOfConductAgreementId() { + this(null, null); + } +} diff --git a/src/main/resources/config/liquibase/changelog/20230908050001_changelog.xml b/src/main/resources/config/liquibase/changelog/20230908050001_changelog.xml deleted file mode 100644 index ad0ef4c6f239..000000000000 --- a/src/main/resources/config/liquibase/changelog/20230908050001_changelog.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml index 1b98b804c63c..2c6bc996a14b 100644 --- a/src/main/resources/config/liquibase/master.xml +++ b/src/main/resources/config/liquibase/master.xml @@ -61,7 +61,6 @@ - From 1a7e7089c96d9cddb5cb328e82e3f0a5a25a500e Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 21 Sep 2023 12:00:35 +0200 Subject: [PATCH 47/76] Migration and fix CourseCodeOfConductAgreementId member cannot be final --- .../CourseCodeOfConductAgreementId.java | 49 ++++++++++++++++--- .../changelog/20230919184052_changelog.xml | 29 +++++++++++ .../resources/config/liquibase/master.xml | 1 + 3 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 src/main/resources/config/liquibase/changelog/20230919184052_changelog.xml diff --git a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java index defc96808beb..a04ae68a1f5c 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java @@ -1,17 +1,54 @@ package de.tum.in.www1.artemis.domain; import java.io.Serializable; +import java.util.Objects; /** * The primary key for CourseCodeOfConductAgreement - * - * @param course the course's id - * @param user the user's id */ -record CourseCodeOfConductAgreementId(Long course, Long user) implements Serializable { +class CourseCodeOfConductAgreementId implements Serializable { + + private Long course; + + private Long user; + + CourseCodeOfConductAgreementId(Long course, Long user) { + this.course = course; + this.user = user; + } - // Needed for JPA CourseCodeOfConductAgreementId() { - this(null, null); + // Needed for JPA + } + + public Long getCourse() { + return course; + } + + public void setCourse(Long course) { + this.course = course; + } + + public Long getUser() { + return user; + } + + public void setUser(Long user) { + this.user = user; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + CourseCodeOfConductAgreementId that = (CourseCodeOfConductAgreementId) o; + return course.equals(that.course) && user.equals(that.user); + } + + @Override + public int hashCode() { + return Objects.hash(course, user); } } diff --git a/src/main/resources/config/liquibase/changelog/20230919184052_changelog.xml b/src/main/resources/config/liquibase/changelog/20230919184052_changelog.xml new file mode 100644 index 000000000000..d7536bb12d20 --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/20230919184052_changelog.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml index 2c6bc996a14b..0f9bdb2e6984 100644 --- a/src/main/resources/config/liquibase/master.xml +++ b/src/main/resources/config/liquibase/master.xml @@ -61,6 +61,7 @@ + From 45fbbc7319e6e8bb6df676f8b676671e698f4896 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 21 Sep 2023 15:09:03 +0200 Subject: [PATCH 48/76] Address comments: - https://github.com/ls1intum/Artemis/pull/7154#discussion_r1327942622 - https://github.com/ls1intum/Artemis/pull/7154#discussion_r1331073524 - https://github.com/ls1intum/Artemis/pull/7154#discussion_r1331064742 - https://github.com/ls1intum/Artemis/pull/7154#discussion_r1331065549 - https://github.com/ls1intum/Artemis/pull/7154#discussion_r1331068832 - https://github.com/ls1intum/Artemis/pull/7154#discussion_r1331073524 - https://github.com/ls1intum/Artemis/pull/7154#discussion_r1331075232 --- .../domain/CourseCodeOfConductAgreement.java | 17 +++----------- ...ourseCodeOfConductAgreementRepository.java | 20 +++++++++------- .../CourseCodeOfConductAgreementService.java | 23 +++++-------------- .../conversation/ConversationResource.java | 2 +- .../changelog/20230919184052_changelog.xml | 1 - ...nversations-code-of-conduct.component.html | 2 +- ...conversations-code-of-conduct.component.ts | 11 --------- ...urseCodeOfConductAgreementServiceTest.java | 6 ++++- 8 files changed, 28 insertions(+), 54 deletions(-) diff --git a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java index 304010212e6f..00efa31e35cc 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java @@ -25,9 +25,6 @@ public class CourseCodeOfConductAgreement { @JoinColumn(name = "user_id") private User user; - @Column(name = "is_accepted") - private boolean isAccepted = false; - public Course getCourse() { return course; } @@ -44,14 +41,6 @@ public void setUser(User user) { this.user = user; } - public boolean getIsAccepted() { - return isAccepted; - } - - public void setIsAccepted(boolean isAccepted) { - this.isAccepted = isAccepted; - } - @Override public boolean equals(Object o) { if (this == o) @@ -61,16 +50,16 @@ public boolean equals(Object o) { if (!super.equals(o)) return false; CourseCodeOfConductAgreement that = (CourseCodeOfConductAgreement) o; - return course.equals(that.course) && user.equals(that.user) && isAccepted == that.isAccepted; + return course.equals(that.course) && user.equals(that.user); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), course, user, isAccepted); + return Objects.hash(super.hashCode(), course, user); } @Override public String toString() { - return "CourseCodeOfConductAgreement{" + "course=" + course + ", user=" + user + ", isAccepted=" + isAccepted + '}'; + return "CourseCodeOfConductAgreement{" + "course=" + course + ", user=" + user + '}'; } } diff --git a/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java index 27a8c55af256..4e70e84b8918 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java @@ -3,9 +3,9 @@ import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import de.tum.in.www1.artemis.domain.CourseCodeOfConductAgreement; @@ -22,10 +22,14 @@ public interface CourseCodeOfConductAgreementRepository extends JpaRepository findByCourseIdAndUserId(@Param("courseId") Long courseId, @Param("userId") Long userId); + Optional findByCourseIdAndUserId(Long courseId, Long userId); + + /** + * Delete all users' agreements to a course's code of conduct. + * + * @param courseId the ID of the code of conduct's course + */ + @Transactional // ok because of delete + @Modifying + void deleteByCourseId(Long courseId); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java b/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java index d178075b3fb2..85ae28277999 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java @@ -1,7 +1,5 @@ package de.tum.in.www1.artemis.service; -import java.util.Optional; - import org.springframework.stereotype.Service; import de.tum.in.www1.artemis.domain.Course; @@ -29,18 +27,7 @@ public class CourseCodeOfConductAgreementService { * @return if the user agreed to the course's code of conduct */ public boolean fetchUserAgreesToCodeOfConductInCourse(User user, Course course) { - Optional courseCodeOfConductAgreement = courseCodeOfConductAgreementRepository.findByCourseIdAndUserId(course.getId(), user.getId()); - if (courseCodeOfConductAgreement.isPresent()) { - return courseCodeOfConductAgreement.get().getIsAccepted(); - } - else { - CourseCodeOfConductAgreement courseCodeOfConductAgreementNew = new CourseCodeOfConductAgreement(); - courseCodeOfConductAgreementNew.setCourse(course); - courseCodeOfConductAgreementNew.setUser(user); - courseCodeOfConductAgreementNew.setIsAccepted(false); - courseCodeOfConductAgreementRepository.save(courseCodeOfConductAgreementNew); - return courseCodeOfConductAgreementNew.getIsAccepted(); - } + return courseCodeOfConductAgreementRepository.findByCourseIdAndUserId(course.getId(), user.getId()).isPresent(); } /** @@ -50,11 +37,13 @@ public boolean fetchUserAgreesToCodeOfConductInCourse(User user, Course course) * @param course the code of conduct's course */ public void setUserAgreesToCodeOfConductInCourse(User user, Course course) { - CourseCodeOfConductAgreement courseCodeOfConductAgreement = courseCodeOfConductAgreementRepository.findByCourseIdAndUserId(course.getId(), user.getId()) - .orElse(new CourseCodeOfConductAgreement()); + CourseCodeOfConductAgreement courseCodeOfConductAgreement = new CourseCodeOfConductAgreement(); courseCodeOfConductAgreement.setCourse(course); courseCodeOfConductAgreement.setUser(user); - courseCodeOfConductAgreement.setIsAccepted(true); courseCodeOfConductAgreementRepository.save(courseCodeOfConductAgreement); } + + public void resetUsersAgreeToCodeOfConductInCourse(Course course) { + courseCodeOfConductAgreementRepository.deleteByCourseId(course.getId()); + } } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java index bfe452621e4b..98d7cf612f65 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java @@ -159,7 +159,7 @@ public ResponseEntity acceptCodeOfConduct(@PathVariable Long courseId) var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, courseRepository.findByIdElseThrow(courseId), requestingUser); courseCodeOfConductAgreementService.setUserAgreesToCodeOfConductInCourse(requestingUser, course); - return ResponseEntity.ok(courseCodeOfConductAgreementService.fetchUserAgreesToCodeOfConductInCourse(requestingUser, course)); + return ResponseEntity.ok(true); } /** diff --git a/src/main/resources/config/liquibase/changelog/20230919184052_changelog.xml b/src/main/resources/config/liquibase/changelog/20230919184052_changelog.xml index d7536bb12d20..ccc79d8bfa98 100644 --- a/src/main/resources/config/liquibase/changelog/20230919184052_changelog.xml +++ b/src/main/resources/config/liquibase/changelog/20230919184052_changelog.xml @@ -11,7 +11,6 @@ - diff --git a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html index fd3b7bc97c35..2d3fd39598d2 100644 --- a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html +++ b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html @@ -2,7 +2,7 @@

{{ 'artemisApp.codeOfConduct.title' | artemisTranslate }}

  • - {{ getUserLabel(user) }} + {{ user.name }} ({{ user.email }})
  • diff --git a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts index 783bf08994fa..2a3f6df77d35 100644 --- a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts +++ b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts @@ -33,15 +33,4 @@ export class CourseConversationsCodeOfConductComponent implements OnInit { }); } } - - getUserLabel(user: UserPublicInfoDTO) { - let label = ''; - if (user.firstName) { - label += user.firstName + ' '; - } - if (user.lastName) { - label += user.lastName + ' '; - } - return label.trim(); - } } diff --git a/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementServiceTest.java b/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementServiceTest.java index f81674d894d9..fbc301b55552 100644 --- a/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementServiceTest.java +++ b/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementServiceTest.java @@ -37,7 +37,7 @@ void init() { } @Test - void fetchAndAgreeIsCodeOfConductAccepted() { + void fetchAndAgreeAndResetCodeOfConductAgreement() { var user = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); var course = CourseFactory.generateCourse(null, PAST_TIMESTAMP, FUTURE_TIMESTAMP, new HashSet<>(), "student", "tutor", "editor", "instructor"); courseRepository.save(course); @@ -47,5 +47,9 @@ void fetchAndAgreeIsCodeOfConductAccepted() { courseCodeOfConductAgreementService.setUserAgreesToCodeOfConductInCourse(user, course); var resultAfterAgreement = courseCodeOfConductAgreementService.fetchUserAgreesToCodeOfConductInCourse(user, course); assertThat(resultAfterAgreement).isTrue(); + + courseCodeOfConductAgreementService.resetUsersAgreeToCodeOfConductInCourse(course); + var resultAfterReset = courseCodeOfConductAgreementService.fetchUserAgreesToCodeOfConductInCourse(user, course); + assertThat(resultAfterReset).isFalse(); } } From 4d662bfa383f0fbe620d3c998b95ce17c738cbac Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 21 Sep 2023 15:19:30 +0200 Subject: [PATCH 49/76] Address comment: - https://github.com/ls1intum/Artemis/pull/7154#discussion_r1331075232 --- src/main/webapp/i18n/de/course.json | 6 +----- src/main/webapp/i18n/de/courseCodeOfConduct.json | 2 +- src/main/webapp/i18n/en/course.json | 6 +----- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/webapp/i18n/de/course.json b/src/main/webapp/i18n/de/course.json index 3390637f6d32..642561a24be8 100644 --- a/src/main/webapp/i18n/de/course.json +++ b/src/main/webapp/i18n/de/course.json @@ -93,11 +93,7 @@ }, "messagingEnabled": { "label": "Nachrichten aktiviert", - "tooltip": "Ermöglicht den Nachrichtenaustausch zwischen Nutzer:innen in privaten oder öffentlichen Kanälen, Gruppenchats oder Direktnachrichten. Kanäle können nur von Lehrenden und Tutor:innen erstellt werden. Nutzer:innen können selbst öffentlichen Kanälen beitreten und müssen zu privaten Kanälen hinzugefügt werden. Alle Nutzer:innen können einen privaten Gruppenchat starten und andere Nutzer:innen hinzufügen. Ein Gruppenchat ist auf zehn Mitglieder:innen begrenzt. Alle Nutzer:innen können Direktnachrichten an andere Nutzer:innen senden. Die Chats finden im Nachrichtenbereich des Kurses statt.", - "codeOfConduct": { - "label": "Verhaltenskodex", - "tooltip": "Der Verhaltenskodex gibt Nutzer:innen an, wie sie miteinander kommunizieren sollen und welche Konsequenzen bei Fehlverhalten drohen können, sowie einen Kontakt zur Berichterstattung." - } + "tooltip": "Ermöglicht den Nachrichtenaustausch zwischen Nutzer:innen in privaten oder öffentlichen Kanälen, Gruppenchats oder Direktnachrichten. Kanäle können nur von Lehrenden und Tutor:innen erstellt werden. Nutzer:innen können selbst öffentlichen Kanälen beitreten und müssen zu privaten Kanälen hinzugefügt werden. Alle Nutzer:innen können einen privaten Gruppenchat starten und andere Nutzer:innen hinzufügen. Ein Gruppenchat ist auf zehn Mitglieder:innen begrenzt. Alle Nutzer:innen können Direktnachrichten an andere Nutzer:innen senden. Die Chats finden im Nachrichtenbereich des Kurses statt." } }, "registrationEnabled": { diff --git a/src/main/webapp/i18n/de/courseCodeOfConduct.json b/src/main/webapp/i18n/de/courseCodeOfConduct.json index 52cd1b76335a..81514c7421f8 100644 --- a/src/main/webapp/i18n/de/courseCodeOfConduct.json +++ b/src/main/webapp/i18n/de/courseCodeOfConduct.json @@ -3,7 +3,7 @@ "codeOfConduct": { "accept": "Akzeptieren", "title": "Verhaltenskodex", - "tooltip": "Der Verhaltenskodex gibt Nutzer:innen an, wie sie miteinander kommunizieren sollen und welche Konsequenzen bei Fehlverhalten drohen können, sowie einen Kontakt zur Berichterstattung." + "tooltip": "Der Verhaltenskodex gibt Nutzer:innen an, wie sie miteinander kommunizieren sollen und welche Konsequenzen bei Fehlverhalten drohen können, sowie einen Kontakt zum Melden von Verstößen." } } } diff --git a/src/main/webapp/i18n/en/course.json b/src/main/webapp/i18n/en/course.json index 6e8695f31d42..978785393729 100644 --- a/src/main/webapp/i18n/en/course.json +++ b/src/main/webapp/i18n/en/course.json @@ -93,11 +93,7 @@ }, "messagingEnabled": { "label": "Messaging Enabled", - "tooltip": "Enables messaging between course users in private or public channels, group chats or direct messages. Channels can only be created by instructors and tutors. Users can self-join public channels and must be invited to private channels. Every user can start a private group chat and add other users. A group chat is limited to 10 members. Every user can start a private one-to-one chat with another user. The chats happens in the messaging space of the course.", - "codeOfConduct": { - "label": "Code of Conduct", - "tooltip": "The Code of Conduct describes to users how best to communicate and which consequences might be raised if there is misconduct, as well as, contact information for reporting." - } + "tooltip": "Enables messaging between course users in private or public channels, group chats or direct messages. Channels can only be created by instructors and tutors. Users can self-join public channels and must be invited to private channels. Every user can start a private group chat and add other users. A group chat is limited to 10 members. Every user can start a private one-to-one chat with another user. The chats happens in the messaging space of the course." } }, "registrationEnabled": { From d64851c5fff23aa098892bfefc4b244e3d84bbf5 Mon Sep 17 00:00:00 2001 From: Tim Ortel <100865202+TimOrtel@users.noreply.github.com> Date: Thu, 21 Sep 2023 15:33:57 +0200 Subject: [PATCH 50/76] Introduced new responsible users endpoint. Refactor urls. --- .../service/dto/ResponsibleUserDTO.java | 26 +++++++++++++ .../service/dto/UserPublicInfoDTO.java | 11 ------ .../conversation/ConversationResource.java | 39 ++++++++++++++----- ...nversations-code-of-conduct.component.html | 3 +- ...conversations-code-of-conduct.component.ts | 21 +++------- .../conversations/conversation.service.ts | 10 +++-- .../metis/metis-conversation.service.ts | 8 ++-- .../metis/ConversationIntegrationTest.java | 35 ++++++++++++----- 8 files changed, 97 insertions(+), 56 deletions(-) create mode 100644 src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java diff --git a/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java b/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java new file mode 100644 index 000000000000..08614830bf24 --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java @@ -0,0 +1,26 @@ +package de.tum.in.www1.artemis.service.dto; + +import java.util.Objects; + +public record ResponsibleUserDTO(String firstName, String lastName, String email) { + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + ResponsibleUserDTO that = (ResponsibleUserDTO) o; + return Objects.equals(firstName, that.firstName) && Objects.equals(lastName, that.lastName) && Objects.equals(email, that.email); + } + + @Override + public int hashCode() { + return Objects.hash(firstName, lastName, email); + } + + @Override + public String toString() { + return "ResponsibleUserDTO{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", email='" + email + '\'' + '}'; + } +} diff --git a/src/main/java/de/tum/in/www1/artemis/service/dto/UserPublicInfoDTO.java b/src/main/java/de/tum/in/www1/artemis/service/dto/UserPublicInfoDTO.java index 80a36881ee09..73f432f608c0 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/dto/UserPublicInfoDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/dto/UserPublicInfoDTO.java @@ -25,8 +25,6 @@ public class UserPublicInfoDTO { private String lastName; - private String email; - private Boolean isInstructor; private Boolean isEditor; @@ -45,7 +43,6 @@ public UserPublicInfoDTO(User user) { this.name = user.getName(); this.firstName = user.getFirstName(); this.lastName = user.getLastName(); - this.email = user.getEmail(); } /** @@ -104,14 +101,6 @@ public void setLastName(String lastName) { this.lastName = lastName; } - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - public Boolean getIsInstructor() { return isInstructor; } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java index bfe452621e4b..5586ee1fb65f 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java @@ -1,8 +1,6 @@ package de.tum.in.www1.artemis.web.rest.metis.conversation; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,6 +21,8 @@ import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastStudent; import de.tum.in.www1.artemis.service.AuthorizationCheckService; import de.tum.in.www1.artemis.service.CourseCodeOfConductAgreementService; +import de.tum.in.www1.artemis.service.CourseService; +import de.tum.in.www1.artemis.service.dto.ResponsibleUserDTO; import de.tum.in.www1.artemis.service.dto.UserPublicInfoDTO; import de.tum.in.www1.artemis.service.metis.conversation.ConversationService; import de.tum.in.www1.artemis.service.metis.conversation.ConversationService.ConversationMemberSearchFilters; @@ -49,15 +49,18 @@ public class ConversationResource extends ConversationManagementResource { private final UserRepository userRepository; + private final CourseService courseService; + public ConversationResource(ConversationService conversationService, ChannelAuthorizationService channelAuthorizationService, AuthorizationCheckService authorizationCheckService, UserRepository userRepository, CourseRepository courseRepository, - CourseCodeOfConductAgreementService courseCodeOfConductAgreementService) { + CourseCodeOfConductAgreementService courseCodeOfConductAgreementService, CourseService courseService) { super(courseRepository); this.conversationService = conversationService; this.channelAuthorizationService = channelAuthorizationService; this.authorizationCheckService = authorizationCheckService; this.courseCodeOfConductAgreementService = courseCodeOfConductAgreementService; this.userRepository = userRepository; + this.courseService = courseService; } /** @@ -130,12 +133,12 @@ public ResponseEntity hasUnreadMessages(@PathVariable Long courseId) { } /** - * GET /api/courses/:courseId/code-of-conduct-agreement : Checks if the user agrees to the code of conduct + * GET /api/courses/:courseId/code-of-conduct/agreement : Checks if the user agrees to the code of conduct * * @param courseId the course's ID * @return ResponseEntity with status 200 (Ok) and body is true if the user agreed to the course's code of conduct */ - @GetMapping("/{courseId}/code-of-conduct-agreement") + @GetMapping("/{courseId}/code-of-conduct/agreement") @EnforceAtLeastStudent public ResponseEntity isCodeOfConductAccepted(@PathVariable Long courseId) { checkMessagingEnabledElseThrow(courseId); @@ -146,20 +149,36 @@ public ResponseEntity isCodeOfConductAccepted(@PathVariable Long course } /** - * POST /api/courses/:courseId/code-of-conduct-agreement : Accept the course's code of conduct + * POST /api/courses/:courseId/code-of-conduct/agreement : Accept the course's code of conduct * * @param courseId the course's ID * @return ResponseEntity with status 200 (Ok) and body is true if the user agreed to the code of conduct */ - @PostMapping("/{courseId}/code-of-conduct-agreement") + @PatchMapping("/{courseId}/code-of-conduct/agreement") @EnforceAtLeastStudent - public ResponseEntity acceptCodeOfConduct(@PathVariable Long courseId) { + public ResponseEntity acceptCodeOfConduct(@PathVariable Long courseId) { checkMessagingEnabledElseThrow(courseId); var course = courseRepository.findByIdElseThrow(courseId); var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, courseRepository.findByIdElseThrow(courseId), requestingUser); courseCodeOfConductAgreementService.setUserAgreesToCodeOfConductInCourse(requestingUser, course); - return ResponseEntity.ok(courseCodeOfConductAgreementService.fetchUserAgreesToCodeOfConductInCourse(requestingUser, course)); + return ResponseEntity.ok().build(); + } + + @GetMapping("/{courseId}/code-of-conduct/responsible-users") + @EnforceAtLeastStudent + public ResponseEntity> getResponsibleUsersForCodeOfConduct(@PathVariable Long courseId) { + checkMessagingEnabledElseThrow(courseId); + + var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); + + var course = courseRepository.findByIdElseThrow(courseId); + authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, course, requestingUser); + + var responsibleUsers = userRepository.searchAllByLoginOrNameInGroups(Pageable.unpaged(), "", Set.of(course.getInstructorGroupName())) + .map((user) -> new ResponsibleUserDTO(user.getFirstName(), user.getLastName(), user.getEmail())).toList(); + + return ResponseEntity.ok(responsibleUsers); } /** diff --git a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html index fd3b7bc97c35..b45d7fdf3008 100644 --- a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html +++ b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html @@ -2,8 +2,7 @@

    {{ 'artemisApp.codeOfConduct.title' | artemisTranslate }}

    diff --git a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts index 783bf08994fa..d52e0f193920 100644 --- a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts +++ b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts @@ -1,10 +1,11 @@ import { Component, Input, OnInit } from '@angular/core'; -import { UserPublicInfoDTO } from 'app/core/user/user.model'; +import { User } from 'app/core/user/user.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { onError } from 'app/shared/util/global.utils'; import { AlertService } from 'app/core/util/alert.service'; import { Course } from 'app/entities/course.model'; +import { ConversationService } from 'app/shared/metis/conversations/conversation.service'; @Component({ selector: 'jhi-course-conversations-code-of-conduct', @@ -14,17 +15,18 @@ export class CourseConversationsCodeOfConductComponent implements OnInit { @Input() course: Course; - responsibleContacts: UserPublicInfoDTO[] = []; + responsibleContacts: User[] = []; constructor( private alertService: AlertService, private courseManagementService: CourseManagementService, + private conversationService: ConversationService, ) {} ngOnInit() { if (this.course.id) { - this.courseManagementService.searchUsers(this.course.id, '', ['instructors']).subscribe({ - next: (res: HttpResponse) => { + this.conversationService.getResponsibleUsersForCodeOfConduct(this.course.id).subscribe({ + next: (res: HttpResponse) => { if (res.body) { this.responsibleContacts = res.body; } @@ -33,15 +35,4 @@ export class CourseConversationsCodeOfConductComponent implements OnInit { }); } } - - getUserLabel(user: UserPublicInfoDTO) { - let label = ''; - if (user.firstName) { - label += user.firstName + ' '; - } - if (user.lastName) { - label += user.lastName + ' '; - } - return label.trim(); - } } diff --git a/src/main/webapp/app/shared/metis/conversations/conversation.service.ts b/src/main/webapp/app/shared/metis/conversations/conversation.service.ts index 9c52048d4824..ce3ff8d49a3b 100644 --- a/src/main/webapp/app/shared/metis/conversations/conversation.service.ts +++ b/src/main/webapp/app/shared/metis/conversations/conversation.service.ts @@ -122,12 +122,16 @@ export class ConversationService { return this.http.get(`${this.resourceUrl}${courseId}/unread-messages`, { observe: 'response' }); } - acceptCodeOfConduct(courseId: number): Observable> { - return this.http.post(`${this.resourceUrl}${courseId}/code-of-conduct-agreement`, null, { observe: 'response' }); + acceptCodeOfConduct(courseId: number): Observable> { + return this.http.patch(`${this.resourceUrl}${courseId}/code-of-conduct/agreement`, null, { observe: 'response' }); } checkIsCodeOfConductAccepted(courseId: number): Observable> { - return this.http.get(`${this.resourceUrl}${courseId}/code-of-conduct-agreement`, { observe: 'response' }); + return this.http.get(`${this.resourceUrl}${courseId}/code-of-conduct/agreement`, { observe: 'response' }); + } + + getResponsibleUsersForCodeOfConduct(courseId: number): Observable> { + return this.http.get(`${this.resourceUrl}${courseId}/code-of-conduct/responsible-users`, { observe: 'response' }); } public convertDateFromClient = (conversation: Conversation) => ({ 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 6269607f599d..de0f785ccad4 100644 --- a/src/main/webapp/app/shared/metis/metis-conversation.service.ts +++ b/src/main/webapp/app/shared/metis/metis-conversation.service.ts @@ -262,11 +262,9 @@ export class MetisConversationService implements OnDestroy { } this.conversationService.acceptCodeOfConduct(course.id).subscribe({ - next: (response) => { - if (response.body !== null) { - this.isCodeOfConductAccepted = response.body; - this._isCodeOfConductAccepted$.next(this.isCodeOfConductAccepted); - } + next: () => { + this.isCodeOfConductAccepted = true; + this._isCodeOfConductAccepted$.next(true); }, error: (errorResponse: HttpErrorResponse) => { onError(this.alertService, errorResponse); diff --git a/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java index 4fe7ce58eed7..baf70fab9e6e 100644 --- a/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java @@ -14,9 +14,7 @@ import org.springframework.security.test.context.support.WithMockUser; import org.springframework.util.LinkedMultiValueMap; -import de.tum.in.www1.artemis.domain.Course; -import de.tum.in.www1.artemis.domain.Lecture; -import de.tum.in.www1.artemis.domain.TextExercise; +import de.tum.in.www1.artemis.domain.*; import de.tum.in.www1.artemis.domain.enumeration.CourseInformationSharingConfiguration; import de.tum.in.www1.artemis.domain.exam.Exam; import de.tum.in.www1.artemis.domain.metis.conversation.Channel; @@ -25,6 +23,7 @@ import de.tum.in.www1.artemis.exercise.textexercise.TextExerciseUtilService; import de.tum.in.www1.artemis.lecture.LectureUtilService; import de.tum.in.www1.artemis.post.ConversationUtilService; +import de.tum.in.www1.artemis.service.dto.ResponsibleUserDTO; import de.tum.in.www1.artemis.user.UserFactory; import de.tum.in.www1.artemis.web.rest.metis.conversation.dtos.*; @@ -42,6 +41,8 @@ class ConversationIntegrationTest extends AbstractConversationTest { private final ConversationUtilService conversationUtilService; + private List users = List.of(); + @Autowired public ConversationIntegrationTest(TextExerciseUtilService textExerciseUtilService, ExerciseUtilService exerciseUtilService, ExamUtilService examUtilService, LectureUtilService lectureUtilService, ConversationUtilService conversationUtilService) { @@ -55,9 +56,12 @@ public ConversationIntegrationTest(TextExerciseUtilService textExerciseUtilServi @BeforeEach void setupTestScenario() throws Exception { super.setupTestScenario(); - userUtilService.addUsers(TEST_PREFIX, 1, 1, 1, 1); + users = userUtilService.addUsers(TEST_PREFIX, 1, 1, 1, 1); if (userRepository.findOneByLogin(testPrefix + "student42").isEmpty()) { - userRepository.save(UserFactory.generateActivatedUser(testPrefix + "student42")); + User student42 = UserFactory.generateActivatedUser(testPrefix + "student42"); + userRepository.save(student42); + + users.add(student42); } } @@ -431,20 +435,31 @@ void unreadMessages_shouldReturnCorrectValue_Message() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") void codeOfConduct_isAccepted() throws Exception { - var agreement = request.get("/api/courses/" + exampleCourseId + "/code-of-conduct-agreement", HttpStatus.OK, Boolean.class); + var agreement = request.get("/api/courses/" + exampleCourseId + "/code-of-conduct/agreement", HttpStatus.OK, Boolean.class); assertThat(agreement).isFalse(); } @Test @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") void codeOfConduct_accept() throws Exception { - var agreement = request.postWithResponseBody("/api/courses/" + exampleCourseId + "/code-of-conduct-agreement", HttpStatus.OK, Boolean.class); - assertThat(agreement).isTrue(); + var initialAgreement = request.get("/api/courses/" + exampleCourseId + "/code-of-conduct/agreement", HttpStatus.OK, Boolean.class); + assertThat(initialAgreement).isFalse(); - var newAgreement = request.get("/api/courses/" + exampleCourseId + "/code-of-conduct-agreement", HttpStatus.OK, Boolean.class); + // Accept + request.patch("/api/courses/" + exampleCourseId + "/code-of-conduct/agreement", null, HttpStatus.OK); + + var newAgreement = request.get("/api/courses/" + exampleCourseId + "/code-of-conduct/agreement", HttpStatus.OK, Boolean.class); assertThat(newAgreement).isTrue(); - ; + } + + @Test + @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") + void responsibleUsers_shouldReturnCorrectValue() throws Exception { + var instructors = users.stream().filter((u) -> u.getGroups().contains("instructor")).map((u) -> new ResponsibleUserDTO(u.getFirstName(), u.getLastName(), u.getEmail())) + .toList(); + var responsibleUsers = request.getList("/api/courses/" + exampleCourseId + "/code-of-conduct/responsible-users", HttpStatus.OK, ResponsibleUserDTO.class); + assertThat(responsibleUsers).hasSameElementsAs(instructors).hasSameSizeAs(instructors); } private void assertConversationDTOTransientProperties(ConversationDTO conversationDTO, Boolean isCreator, Boolean isMember, Boolean hasChannelModerationRights, From 93c03ba6ce227e5e0826933d346e971b95f914fa Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 21 Sep 2023 16:54:01 +0200 Subject: [PATCH 51/76] Reset agreements on change and document DTO --- .../www1/artemis/service/dto/ResponsibleUserDTO.java | 3 +++ .../tum/in/www1/artemis/web/rest/CourseResource.java | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java b/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java index 08614830bf24..66460e2dd996 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java @@ -2,6 +2,9 @@ import java.util.Objects; +/** + * A DTO representing a course's responsible contact, i.e., a person to report misconduct to. + */ public record ResponsibleUserDTO(String firstName, String lastName, String email) { @Override diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/CourseResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/CourseResource.java index 7032f50e1064..115e5874d83c 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/CourseResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/CourseResource.java @@ -104,6 +104,8 @@ public class CourseResource { private final GradingScaleRepository gradingScaleRepository; + private final CourseCodeOfConductAgreementService courseCodeOfConductAgreementService; + @Value("${artemis.course-archives-path}") private String courseArchivesDirPath; @@ -114,7 +116,8 @@ public CourseResource(UserRepository userRepository, CourseService courseService TutorParticipationRepository tutorParticipationRepository, SubmissionService submissionService, Optional optionalVcsUserManagementService, AssessmentDashboardService assessmentDashboardService, ExerciseRepository exerciseRepository, Optional optionalCiUserManagementService, FileService fileService, TutorialGroupsConfigurationService tutorialGroupsConfigurationService, GradingScaleService gradingScaleService, - CourseScoreCalculationService courseScoreCalculationService, GradingScaleRepository gradingScaleRepository, LearningPathService learningPathService) { + CourseScoreCalculationService courseScoreCalculationService, GradingScaleRepository gradingScaleRepository, LearningPathService learningPathService, + CourseCodeOfConductAgreementService courseCodeOfConductAgreementService) { this.courseService = courseService; this.courseRepository = courseRepository; this.exerciseService = exerciseService; @@ -134,6 +137,7 @@ public CourseResource(UserRepository userRepository, CourseService courseService this.courseScoreCalculationService = courseScoreCalculationService; this.gradingScaleRepository = gradingScaleRepository; this.learningPathService = learningPathService; + this.courseCodeOfConductAgreementService = courseCodeOfConductAgreementService; } /** @@ -231,6 +235,10 @@ public ResponseEntity updateCourse(@PathVariable Long courseId, @Request } } + if (!courseUpdate.getCourseInformationSharingMessagingCodeOfConduct().equals(existingCourse.getCourseInformationSharingMessagingCodeOfConduct())) { + courseCodeOfConductAgreementService.resetUsersAgreeToCodeOfConductInCourse(existingCourse); + } + courseUpdate.setId(courseId); // Don't persist a wrong ID Course result = courseRepository.save(courseUpdate); From a14964b180c4197d01be840d9205c8315a3c7aeb Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 21 Sep 2023 17:01:31 +0200 Subject: [PATCH 52/76] Address comment: - https://github.com/ls1intum/Artemis/pull/7154#discussion_r1331072057 --- src/main/resources/templates/codeofconduct/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/templates/codeofconduct/README.md b/src/main/resources/templates/codeofconduct/README.md index 2b85beb83449..f934700f9a32 100644 --- a/src/main/resources/templates/codeofconduct/README.md +++ b/src/main/resources/templates/codeofconduct/README.md @@ -24,7 +24,7 @@ We pledge to act and interact in ways that contribute to an open, welcoming, div ## Scope -This Code of Conduct applies within all communication channels. +This Code of Conduct applies within all messages channels. ## Reporting From baeeaf3e73d143a98619b323696bcf092d709b20 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 21 Sep 2023 20:13:50 +0200 Subject: [PATCH 53/76] Address comment: - https://github.com/ls1intum/Artemis/pull/7154#discussion_r1327943437 --- .../course-conversations.component.html | 23 ++++++++----------- .../metis/metis-conversation.service.ts | 3 +++ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.html b/src/main/webapp/app/overview/course-conversations/course-conversations.component.html index 263107ce5781..3d4c0d47cfcf 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.html +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.html @@ -1,36 +1,31 @@ -
    -
    - -
    +
    +
    - +
    -
    -
    - -
    +
    +
    - - + +
    -
    +
    - + />
    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 de0f785ccad4..8f9f864bbc38 100644 --- a/src/main/webapp/app/shared/metis/metis-conversation.service.ts +++ b/src/main/webapp/app/shared/metis/metis-conversation.service.ts @@ -116,6 +116,9 @@ export class MetisConversationService implements OnDestroy { this._isCodeOfConductPresented$.next(this.isCodeOfConductPresented); } + /** + * Set the course conversation component to the contents of the code of conduct. + */ public setCodeOfConduct() { this.activeConversation = undefined; this._activeConversation$.next(this.activeConversation); From dd6b50bd067a6df7830f1051176b14ed4134a218 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 21 Sep 2023 20:45:34 +0200 Subject: [PATCH 54/76] On review --- .../service/CourseCodeOfConductAgreementService.java | 5 +++++ .../www1/artemis/service/dto/ResponsibleUserDTO.java | 10 +++++----- .../metis/conversation/ConversationResource.java | 12 +++++++++--- ...urse-conversations-code-of-conduct.component.html | 2 +- .../course-conversations.component.html | 12 ++++++------ .../artemis/metis/ConversationIntegrationTest.java | 3 +-- 6 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java b/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java index 85ae28277999..4fcddedd9688 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java @@ -43,6 +43,11 @@ public void setUserAgreesToCodeOfConductInCourse(User user, Course course) { courseCodeOfConductAgreementRepository.save(courseCodeOfConductAgreement); } + /** + * Reset all agreements to a course's code of conduct. + * + * @param course the code of conduct's course + */ public void resetUsersAgreeToCodeOfConductInCourse(Course course) { courseCodeOfConductAgreementRepository.deleteByCourseId(course.getId()); } diff --git a/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java b/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java index 66460e2dd996..c1df755e328f 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java @@ -3,9 +3,9 @@ import java.util.Objects; /** - * A DTO representing a course's responsible contact, i.e., a person to report misconduct to. + * A DTO representing a course's responsible user, i.e., a person to report misconduct to. */ -public record ResponsibleUserDTO(String firstName, String lastName, String email) { +public record ResponsibleUserDTO(String name, String email) { @Override public boolean equals(Object o) { @@ -14,16 +14,16 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; ResponsibleUserDTO that = (ResponsibleUserDTO) o; - return Objects.equals(firstName, that.firstName) && Objects.equals(lastName, that.lastName) && Objects.equals(email, that.email); + return Objects.equals(name, that.name) && Objects.equals(email, that.email); } @Override public int hashCode() { - return Objects.hash(firstName, lastName, email); + return Objects.hash(name, email); } @Override public String toString() { - return "ResponsibleUserDTO{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", email='" + email + '\'' + '}'; + return "ResponsibleUserDTO{" + "name='" + name + '\'' + ", email='" + email + '\'' + '}'; } } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java index 5586ee1fb65f..d5366e985630 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java @@ -149,10 +149,10 @@ public ResponseEntity isCodeOfConductAccepted(@PathVariable Long course } /** - * POST /api/courses/:courseId/code-of-conduct/agreement : Accept the course's code of conduct + * PATCH /api/courses/:courseId/code-of-conduct/agreement : Accept the course's code of conduct * * @param courseId the course's ID - * @return ResponseEntity with status 200 (Ok) and body is true if the user agreed to the code of conduct + * @return ResponseEntity with status 200 (Ok) */ @PatchMapping("/{courseId}/code-of-conduct/agreement") @EnforceAtLeastStudent @@ -165,6 +165,12 @@ public ResponseEntity acceptCodeOfConduct(@PathVariable Long courseId) { return ResponseEntity.ok().build(); } + /** + * GET /api/courses/:courseId/code-of-conduct/responsible-users : Users responsible for the course + * + * @param courseId the course's ID + * @return ResponseEntity with the status 200 (Ok) and a list of users responsible for the course + */ @GetMapping("/{courseId}/code-of-conduct/responsible-users") @EnforceAtLeastStudent public ResponseEntity> getResponsibleUsersForCodeOfConduct(@PathVariable Long courseId) { @@ -176,7 +182,7 @@ public ResponseEntity> getResponsibleUsersForCodeOfCond authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, course, requestingUser); var responsibleUsers = userRepository.searchAllByLoginOrNameInGroups(Pageable.unpaged(), "", Set.of(course.getInstructorGroupName())) - .map((user) -> new ResponsibleUserDTO(user.getFirstName(), user.getLastName(), user.getEmail())).toList(); + .map((user) -> new ResponsibleUserDTO(user.getName(), user.getEmail())).toList(); return ResponseEntity.ok(responsibleUsers); } diff --git a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html index b45d7fdf3008..944577d640b4 100644 --- a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html +++ b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.html @@ -2,7 +2,7 @@

    {{ 'artemisApp.codeOfConduct.title' | artemisTranslate }}

    diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.html b/src/main/webapp/app/overview/course-conversations/course-conversations.component.html index 3d4c0d47cfcf..0de393cd8600 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.html +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.html @@ -1,21 +1,21 @@
    - +
    - +
    - +
    - - + +
    @@ -25,7 +25,7 @@ [readOnlyMode]="!!getAsChannel(activeConversation)?.isArchived" [activePost]="postInThread" (closePostThread)="postInThread = undefined" - /> + >
    diff --git a/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java index baf70fab9e6e..2e1a959b1e98 100644 --- a/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/metis/ConversationIntegrationTest.java @@ -455,8 +455,7 @@ void codeOfConduct_accept() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") void responsibleUsers_shouldReturnCorrectValue() throws Exception { - var instructors = users.stream().filter((u) -> u.getGroups().contains("instructor")).map((u) -> new ResponsibleUserDTO(u.getFirstName(), u.getLastName(), u.getEmail())) - .toList(); + var instructors = users.stream().filter((u) -> u.getGroups().contains("instructor")).map((u) -> new ResponsibleUserDTO(u.getName(), u.getEmail())).toList(); var responsibleUsers = request.getList("/api/courses/" + exampleCourseId + "/code-of-conduct/responsible-users", HttpStatus.OK, ResponsibleUserDTO.class); assertThat(responsibleUsers).hasSameElementsAs(instructors).hasSameSizeAs(instructors); From 35bf2d4a550ff8d2f8281e11f0c0d9ed101c5dc5 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 27 Sep 2023 12:37:45 +0200 Subject: [PATCH 55/76] Fix error; address comments: - Error: incompatible types: String cannot be converted to Path - Remove auto-generated overrides: https://github.com/ls1intum/Artemis/pull/7154/files#r1334981359 - Reuse course: https://github.com/ls1intum/Artemis/pull/7154/files#r1334982232 - Reuse course: https://github.com/ls1intum/Artemis/pull/7154/files#r1334982315 --- .../service/dto/ResponsibleUserDTO.java | 22 ------------------- .../www1/artemis/web/rest/FileResource.java | 3 +-- .../conversation/ConversationResource.java | 14 +++++------- 3 files changed, 6 insertions(+), 33 deletions(-) diff --git a/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java b/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java index c1df755e328f..59bf474332ca 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/service/dto/ResponsibleUserDTO.java @@ -1,29 +1,7 @@ package de.tum.in.www1.artemis.service.dto; -import java.util.Objects; - /** * A DTO representing a course's responsible user, i.e., a person to report misconduct to. */ public record ResponsibleUserDTO(String name, String email) { - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - ResponsibleUserDTO that = (ResponsibleUserDTO) o; - return Objects.equals(name, that.name) && Objects.equals(email, that.email); - } - - @Override - public int hashCode() { - return Objects.hash(name, email); - } - - @Override - public String toString() { - return "ResponsibleUserDTO{" + "name='" + name + '\'' + ", email='" + email + '\'' + '}'; - } } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java index 4abe0531f5cf..44f776acc1e2 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java @@ -319,8 +319,7 @@ public ResponseEntity getCourseCodeOfConduct() throws IOException { var templatePath = Path.of("templates", "codeofconduct", "README.md"); log.debug("REST request to get template : {}", templatePath); var resource = resourceLoaderService.getResource(templatePath); - var path = resource.getFile().getPath(); - return responseEntityForFilePath(path); + return responseEntityForFilePath(Path.of(resource.getURI().getPath())); } /** diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java index d5366e985630..5f816b92b946 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java @@ -21,7 +21,6 @@ import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastStudent; import de.tum.in.www1.artemis.service.AuthorizationCheckService; import de.tum.in.www1.artemis.service.CourseCodeOfConductAgreementService; -import de.tum.in.www1.artemis.service.CourseService; import de.tum.in.www1.artemis.service.dto.ResponsibleUserDTO; import de.tum.in.www1.artemis.service.dto.UserPublicInfoDTO; import de.tum.in.www1.artemis.service.metis.conversation.ConversationService; @@ -45,22 +44,19 @@ public class ConversationResource extends ConversationManagementResource { private final AuthorizationCheckService authorizationCheckService; - private final CourseCodeOfConductAgreementService courseCodeOfConductAgreementService; - private final UserRepository userRepository; - private final CourseService courseService; + private final CourseCodeOfConductAgreementService courseCodeOfConductAgreementService; public ConversationResource(ConversationService conversationService, ChannelAuthorizationService channelAuthorizationService, AuthorizationCheckService authorizationCheckService, UserRepository userRepository, CourseRepository courseRepository, - CourseCodeOfConductAgreementService courseCodeOfConductAgreementService, CourseService courseService) { + CourseCodeOfConductAgreementService courseCodeOfConductAgreementService) { super(courseRepository); this.conversationService = conversationService; this.channelAuthorizationService = channelAuthorizationService; this.authorizationCheckService = authorizationCheckService; - this.courseCodeOfConductAgreementService = courseCodeOfConductAgreementService; this.userRepository = userRepository; - this.courseService = courseService; + this.courseCodeOfConductAgreementService = courseCodeOfConductAgreementService; } /** @@ -144,7 +140,7 @@ public ResponseEntity isCodeOfConductAccepted(@PathVariable Long course checkMessagingEnabledElseThrow(courseId); var course = courseRepository.findByIdElseThrow(courseId); var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); - authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, courseRepository.findByIdElseThrow(courseId), requestingUser); + authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, course, requestingUser); return ResponseEntity.ok(courseCodeOfConductAgreementService.fetchUserAgreesToCodeOfConductInCourse(requestingUser, course)); } @@ -160,7 +156,7 @@ public ResponseEntity acceptCodeOfConduct(@PathVariable Long courseId) { checkMessagingEnabledElseThrow(courseId); var course = courseRepository.findByIdElseThrow(courseId); var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); - authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, courseRepository.findByIdElseThrow(courseId), requestingUser); + authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, course, requestingUser); courseCodeOfConductAgreementService.setUserAgreesToCodeOfConductInCourse(requestingUser, course); return ResponseEntity.ok().build(); } From 0f80c151ebe75fdc2c3ab02640561b8977893adf Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 27 Sep 2023 18:00:50 +0200 Subject: [PATCH 56/76] Address comments: - Don't hash super: https://github.com/ls1intum/Artemis/pull/7154#discussion_r1334980513 - Correct ID: https://github.com/ls1intum/Artemis/pull/7154#discussion_r1334981033 - Let resource load content: https://github.com/ls1intum/Artemis/pull/7154#discussion_r1334982139 - Human-readable names: https://github.com/ls1intum/Artemis/pull/7154#discussion_r1334986256 - Hide note: https://github.com/ls1intum/Artemis/pull/7154#discussion_r1334990082 --- .../artemis/domain/CourseCodeOfConductAgreement.java | 2 +- .../artemis/domain/CourseCodeOfConductAgreementId.java | 2 +- .../CourseCodeOfConductAgreementRepository.java | 3 ++- .../de/tum/in/www1/artemis/web/rest/FileResource.java | 2 +- ...4052_changelog.xml => 20230927125606_changelog.xml} | 10 +++------- src/main/resources/config/liquibase/master.xml | 2 +- src/main/resources/templates/codeofconduct/README.md | 2 +- 7 files changed, 10 insertions(+), 13 deletions(-) rename src/main/resources/config/liquibase/changelog/{20230919184052_changelog.xml => 20230927125606_changelog.xml} (81%) diff --git a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java index 00efa31e35cc..b63da76657d2 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java @@ -55,7 +55,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(super.hashCode(), course, user); + return Objects.hash(course, user); } @Override diff --git a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java index a04ae68a1f5c..109938bac72e 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java @@ -6,7 +6,7 @@ /** * The primary key for CourseCodeOfConductAgreement */ -class CourseCodeOfConductAgreementId implements Serializable { +public class CourseCodeOfConductAgreementId implements Serializable { private Long course; diff --git a/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java index 4e70e84b8918..e9e18f1b1be7 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java @@ -8,12 +8,13 @@ import org.springframework.transaction.annotation.Transactional; import de.tum.in.www1.artemis.domain.CourseCodeOfConductAgreement; +import de.tum.in.www1.artemis.domain.CourseCodeOfConductAgreementId; /** * Spring Data repository for the Code of Conduct Agreement entity. */ @Repository -public interface CourseCodeOfConductAgreementRepository extends JpaRepository { +public interface CourseCodeOfConductAgreementRepository extends JpaRepository { /** * Find the user's agreement to a course's code of conduct. diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java index 44f776acc1e2..6c3e87d73ad3 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/FileResource.java @@ -319,7 +319,7 @@ public ResponseEntity getCourseCodeOfConduct() throws IOException { var templatePath = Path.of("templates", "codeofconduct", "README.md"); log.debug("REST request to get template : {}", templatePath); var resource = resourceLoaderService.getResource(templatePath); - return responseEntityForFilePath(Path.of(resource.getURI().getPath())); + return ResponseEntity.ok(resource.getInputStream().readAllBytes()); } /** diff --git a/src/main/resources/config/liquibase/changelog/20230919184052_changelog.xml b/src/main/resources/config/liquibase/changelog/20230927125606_changelog.xml similarity index 81% rename from src/main/resources/config/liquibase/changelog/20230919184052_changelog.xml rename to src/main/resources/config/liquibase/changelog/20230927125606_changelog.xml index ccc79d8bfa98..c90303a068a1 100644 --- a/src/main/resources/config/liquibase/changelog/20230919184052_changelog.xml +++ b/src/main/resources/config/liquibase/changelog/20230927125606_changelog.xml @@ -3,7 +3,7 @@ xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:pro="http://www.liquibase.org/xml/ns/pro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.6.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"> - + @@ -12,16 +12,12 @@ - - - - diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml index 1a45cb30992b..2d7584cbf99f 100644 --- a/src/main/resources/config/liquibase/master.xml +++ b/src/main/resources/config/liquibase/master.xml @@ -61,8 +61,8 @@ - + diff --git a/src/main/resources/templates/codeofconduct/README.md b/src/main/resources/templates/codeofconduct/README.md index f934700f9a32..12b09743d756 100644 --- a/src/main/resources/templates/codeofconduct/README.md +++ b/src/main/resources/templates/codeofconduct/README.md @@ -1,4 +1,4 @@ -# Code of Conduct Template: Adapt to your demands + We as students, tutors, and instructors pledge to make participation in our course a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. From 61db09229e46ed7c688a823ad2a3f8cca59e065f Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 28 Sep 2023 12:53:52 +0200 Subject: [PATCH 57/76] Fix server tests https://bamboo.ase.in.tum.de/browse/ARTEMIS-TESTS5385-JAVATEST-23 --- .../java/de/tum/in/www1/artemis/web/rest/CourseResource.java | 2 +- src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/CourseResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/CourseResource.java index 37a6e9a33e7e..252e004026cf 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/CourseResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/CourseResource.java @@ -236,7 +236,7 @@ public ResponseEntity updateCourse(@PathVariable Long courseId, @Request } } - if (!courseUpdate.getCourseInformationSharingMessagingCodeOfConduct().equals(existingCourse.getCourseInformationSharingMessagingCodeOfConduct())) { + if (!Objects.equals(courseUpdate.getCourseInformationSharingMessagingCodeOfConduct(), existingCourse.getCourseInformationSharingMessagingCodeOfConduct())) { courseCodeOfConductAgreementService.resetUsersAgreeToCodeOfConductInCourse(existingCourse); } diff --git a/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java index a9f2a74dd588..dabbd409e0dd 100644 --- a/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java @@ -147,7 +147,7 @@ void testGetCourseIcon() throws Exception { @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void testGetCourseCodeOfConductTemplate() throws Exception { var template = request.get("/api/files/templates/code-of-conduct", HttpStatus.OK, String.class); - assertThat(template).startsWith("# Code of Conduct Template"); + assertThat(template).startsWith(" From 620a530b69ce3ba2a7124faf03a86edde711ed61 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Fri, 6 Oct 2023 14:35:42 +0200 Subject: [PATCH 60/76] Shorten --- ...ctAgreement.java => ConductAgreement.java} | 8 +++---- ...reementId.java => ConductAgreementId.java} | 10 ++++---- ...y.java => ConductAgreementRepository.java} | 8 +++---- ...vice.java => ConductAgreementService.java} | 24 +++++++++---------- .../www1/artemis/web/rest/CourseResource.java | 8 +++---- .../conversation/ConversationResource.java | 12 +++++----- .../changelog/20230927125606_changelog.xml | 14 +++++------ ....java => ConductAgreementServiceTest.java} | 14 +++++------ 8 files changed, 49 insertions(+), 49 deletions(-) rename src/main/java/de/tum/in/www1/artemis/domain/{CourseCodeOfConductAgreement.java => ConductAgreement.java} (85%) rename src/main/java/de/tum/in/www1/artemis/domain/{CourseCodeOfConductAgreementId.java => ConductAgreementId.java} (73%) rename src/main/java/de/tum/in/www1/artemis/repository/{CourseCodeOfConductAgreementRepository.java => ConductAgreementRepository.java} (70%) rename src/main/java/de/tum/in/www1/artemis/service/{CourseCodeOfConductAgreementService.java => ConductAgreementService.java} (52%) rename src/test/java/de/tum/in/www1/artemis/service/{CourseCodeOfConductAgreementServiceTest.java => ConductAgreementServiceTest.java} (67%) diff --git a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java b/src/main/java/de/tum/in/www1/artemis/domain/ConductAgreement.java similarity index 85% rename from src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java rename to src/main/java/de/tum/in/www1/artemis/domain/ConductAgreement.java index b63da76657d2..7d3d608edda3 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreement.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/ConductAgreement.java @@ -10,10 +10,10 @@ * A user's agreement of a course's code of conduct. */ @Entity -@Table(name = "course_code_of_conduct_agreement") +@Table(name = "conduct_agreement") @JsonInclude(JsonInclude.Include.NON_EMPTY) -@IdClass(CourseCodeOfConductAgreementId.class) -public class CourseCodeOfConductAgreement { +@IdClass(ConductAgreementId.class) +public class ConductAgreement { @Id @ManyToOne @@ -49,7 +49,7 @@ public boolean equals(Object o) { return false; if (!super.equals(o)) return false; - CourseCodeOfConductAgreement that = (CourseCodeOfConductAgreement) o; + ConductAgreement that = (ConductAgreement) o; return course.equals(that.course) && user.equals(that.user); } diff --git a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java b/src/main/java/de/tum/in/www1/artemis/domain/ConductAgreementId.java similarity index 73% rename from src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java rename to src/main/java/de/tum/in/www1/artemis/domain/ConductAgreementId.java index 109938bac72e..b233fb485954 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/CourseCodeOfConductAgreementId.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/ConductAgreementId.java @@ -4,20 +4,20 @@ import java.util.Objects; /** - * The primary key for CourseCodeOfConductAgreement + * The primary key for ConductAgreement */ -public class CourseCodeOfConductAgreementId implements Serializable { +public class ConductAgreementId implements Serializable { private Long course; private Long user; - CourseCodeOfConductAgreementId(Long course, Long user) { + ConductAgreementId(Long course, Long user) { this.course = course; this.user = user; } - CourseCodeOfConductAgreementId() { + ConductAgreementId() { // Needed for JPA } @@ -43,7 +43,7 @@ public boolean equals(Object o) { return true; if (o == null || getClass() != o.getClass()) return false; - CourseCodeOfConductAgreementId that = (CourseCodeOfConductAgreementId) o; + ConductAgreementId that = (ConductAgreementId) o; return course.equals(that.course) && user.equals(that.user); } diff --git a/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java b/src/main/java/de/tum/in/www1/artemis/repository/ConductAgreementRepository.java similarity index 70% rename from src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java rename to src/main/java/de/tum/in/www1/artemis/repository/ConductAgreementRepository.java index e9e18f1b1be7..5db886d67638 100644 --- a/src/main/java/de/tum/in/www1/artemis/repository/CourseCodeOfConductAgreementRepository.java +++ b/src/main/java/de/tum/in/www1/artemis/repository/ConductAgreementRepository.java @@ -7,14 +7,14 @@ import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import de.tum.in.www1.artemis.domain.CourseCodeOfConductAgreement; -import de.tum.in.www1.artemis.domain.CourseCodeOfConductAgreementId; +import de.tum.in.www1.artemis.domain.ConductAgreement; +import de.tum.in.www1.artemis.domain.ConductAgreementId; /** * Spring Data repository for the Code of Conduct Agreement entity. */ @Repository -public interface CourseCodeOfConductAgreementRepository extends JpaRepository { +public interface ConductAgreementRepository extends JpaRepository { /** * Find the user's agreement to a course's code of conduct. @@ -23,7 +23,7 @@ public interface CourseCodeOfConductAgreementRepository extends JpaRepository findByCourseIdAndUserId(Long courseId, Long userId); + Optional findByCourseIdAndUserId(Long courseId, Long userId); /** * Delete all users' agreements to a course's code of conduct. diff --git a/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java b/src/main/java/de/tum/in/www1/artemis/service/ConductAgreementService.java similarity index 52% rename from src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java rename to src/main/java/de/tum/in/www1/artemis/service/ConductAgreementService.java index 4fcddedd9688..34da2853543b 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/ConductAgreementService.java @@ -2,21 +2,21 @@ import org.springframework.stereotype.Service; +import de.tum.in.www1.artemis.domain.ConductAgreement; import de.tum.in.www1.artemis.domain.Course; -import de.tum.in.www1.artemis.domain.CourseCodeOfConductAgreement; import de.tum.in.www1.artemis.domain.User; -import de.tum.in.www1.artemis.repository.CourseCodeOfConductAgreementRepository; +import de.tum.in.www1.artemis.repository.ConductAgreementRepository; /** * Service Implementation for managing a user's agreement to a course's code of conduct. */ @Service -public class CourseCodeOfConductAgreementService { +public class ConductAgreementService { - private final CourseCodeOfConductAgreementRepository courseCodeOfConductAgreementRepository; + private final ConductAgreementRepository conductAgreementRepository; - CourseCodeOfConductAgreementService(CourseCodeOfConductAgreementRepository courseCodeOfConductAgreementRepository) { - this.courseCodeOfConductAgreementRepository = courseCodeOfConductAgreementRepository; + ConductAgreementService(ConductAgreementRepository conductAgreementRepository) { + this.conductAgreementRepository = conductAgreementRepository; } /** @@ -27,7 +27,7 @@ public class CourseCodeOfConductAgreementService { * @return if the user agreed to the course's code of conduct */ public boolean fetchUserAgreesToCodeOfConductInCourse(User user, Course course) { - return courseCodeOfConductAgreementRepository.findByCourseIdAndUserId(course.getId(), user.getId()).isPresent(); + return conductAgreementRepository.findByCourseIdAndUserId(course.getId(), user.getId()).isPresent(); } /** @@ -37,10 +37,10 @@ public boolean fetchUserAgreesToCodeOfConductInCourse(User user, Course course) * @param course the code of conduct's course */ public void setUserAgreesToCodeOfConductInCourse(User user, Course course) { - CourseCodeOfConductAgreement courseCodeOfConductAgreement = new CourseCodeOfConductAgreement(); - courseCodeOfConductAgreement.setCourse(course); - courseCodeOfConductAgreement.setUser(user); - courseCodeOfConductAgreementRepository.save(courseCodeOfConductAgreement); + ConductAgreement conductAgreement = new ConductAgreement(); + conductAgreement.setCourse(course); + conductAgreement.setUser(user); + conductAgreementRepository.save(conductAgreement); } /** @@ -49,6 +49,6 @@ public void setUserAgreesToCodeOfConductInCourse(User user, Course course) { * @param course the code of conduct's course */ public void resetUsersAgreeToCodeOfConductInCourse(Course course) { - courseCodeOfConductAgreementRepository.deleteByCourseId(course.getId()); + conductAgreementRepository.deleteByCourseId(course.getId()); } } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/CourseResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/CourseResource.java index 2b2e2b334e58..b87f849158d2 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/CourseResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/CourseResource.java @@ -105,7 +105,7 @@ public class CourseResource { private final GradingScaleRepository gradingScaleRepository; - private final CourseCodeOfConductAgreementService courseCodeOfConductAgreementService; + private final ConductAgreementService conductAgreementService; @Value("${artemis.course-archives-path}") private String courseArchivesDirPath; @@ -118,7 +118,7 @@ public CourseResource(UserRepository userRepository, CourseService courseService AssessmentDashboardService assessmentDashboardService, ExerciseRepository exerciseRepository, Optional optionalCiUserManagementService, FileService fileService, TutorialGroupsConfigurationService tutorialGroupsConfigurationService, GradingScaleService gradingScaleService, CourseScoreCalculationService courseScoreCalculationService, GradingScaleRepository gradingScaleRepository, LearningPathService learningPathService, - CourseCodeOfConductAgreementService courseCodeOfConductAgreementService) { + ConductAgreementService conductAgreementService) { this.courseService = courseService; this.courseRepository = courseRepository; this.exerciseService = exerciseService; @@ -138,7 +138,7 @@ public CourseResource(UserRepository userRepository, CourseService courseService this.courseScoreCalculationService = courseScoreCalculationService; this.gradingScaleRepository = gradingScaleRepository; this.learningPathService = learningPathService; - this.courseCodeOfConductAgreementService = courseCodeOfConductAgreementService; + this.conductAgreementService = conductAgreementService; } /** @@ -238,7 +238,7 @@ public ResponseEntity updateCourse(@PathVariable Long courseId, @Request } if (!Objects.equals(courseUpdate.getCourseInformationSharingMessagingCodeOfConduct(), existingCourse.getCourseInformationSharingMessagingCodeOfConduct())) { - courseCodeOfConductAgreementService.resetUsersAgreeToCodeOfConductInCourse(existingCourse); + conductAgreementService.resetUsersAgreeToCodeOfConductInCourse(existingCourse); } courseUpdate.setId(courseId); // Don't persist a wrong ID diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java index 5f816b92b946..4d057cc015aa 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/metis/conversation/ConversationResource.java @@ -20,7 +20,7 @@ import de.tum.in.www1.artemis.security.Role; import de.tum.in.www1.artemis.security.annotations.EnforceAtLeastStudent; import de.tum.in.www1.artemis.service.AuthorizationCheckService; -import de.tum.in.www1.artemis.service.CourseCodeOfConductAgreementService; +import de.tum.in.www1.artemis.service.ConductAgreementService; import de.tum.in.www1.artemis.service.dto.ResponsibleUserDTO; import de.tum.in.www1.artemis.service.dto.UserPublicInfoDTO; import de.tum.in.www1.artemis.service.metis.conversation.ConversationService; @@ -46,17 +46,17 @@ public class ConversationResource extends ConversationManagementResource { private final UserRepository userRepository; - private final CourseCodeOfConductAgreementService courseCodeOfConductAgreementService; + private final ConductAgreementService conductAgreementService; public ConversationResource(ConversationService conversationService, ChannelAuthorizationService channelAuthorizationService, AuthorizationCheckService authorizationCheckService, UserRepository userRepository, CourseRepository courseRepository, - CourseCodeOfConductAgreementService courseCodeOfConductAgreementService) { + ConductAgreementService conductAgreementService) { super(courseRepository); this.conversationService = conversationService; this.channelAuthorizationService = channelAuthorizationService; this.authorizationCheckService = authorizationCheckService; this.userRepository = userRepository; - this.courseCodeOfConductAgreementService = courseCodeOfConductAgreementService; + this.conductAgreementService = conductAgreementService; } /** @@ -141,7 +141,7 @@ public ResponseEntity isCodeOfConductAccepted(@PathVariable Long course var course = courseRepository.findByIdElseThrow(courseId); var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, course, requestingUser); - return ResponseEntity.ok(courseCodeOfConductAgreementService.fetchUserAgreesToCodeOfConductInCourse(requestingUser, course)); + return ResponseEntity.ok(conductAgreementService.fetchUserAgreesToCodeOfConductInCourse(requestingUser, course)); } /** @@ -157,7 +157,7 @@ public ResponseEntity acceptCodeOfConduct(@PathVariable Long courseId) { var course = courseRepository.findByIdElseThrow(courseId); var requestingUser = userRepository.getUserWithGroupsAndAuthorities(); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.STUDENT, course, requestingUser); - courseCodeOfConductAgreementService.setUserAgreesToCodeOfConductInCourse(requestingUser, course); + conductAgreementService.setUserAgreesToCodeOfConductInCourse(requestingUser, course); return ResponseEntity.ok().build(); } diff --git a/src/main/resources/config/liquibase/changelog/20230927125606_changelog.xml b/src/main/resources/config/liquibase/changelog/20230927125606_changelog.xml index c90303a068a1..b9153d4d9478 100644 --- a/src/main/resources/config/liquibase/changelog/20230927125606_changelog.xml +++ b/src/main/resources/config/liquibase/changelog/20230927125606_changelog.xml @@ -4,20 +4,20 @@ xmlns:pro="http://www.liquibase.org/xml/ns/pro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-4.6.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"> - + - + - + - - diff --git a/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementServiceTest.java b/src/test/java/de/tum/in/www1/artemis/service/ConductAgreementServiceTest.java similarity index 67% rename from src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementServiceTest.java rename to src/test/java/de/tum/in/www1/artemis/service/ConductAgreementServiceTest.java index fbc301b55552..69ff7463478d 100644 --- a/src/test/java/de/tum/in/www1/artemis/service/CourseCodeOfConductAgreementServiceTest.java +++ b/src/test/java/de/tum/in/www1/artemis/service/ConductAgreementServiceTest.java @@ -14,7 +14,7 @@ import de.tum.in.www1.artemis.repository.CourseRepository; import de.tum.in.www1.artemis.user.UserUtilService; -class CourseCodeOfConductAgreementServiceTest extends AbstractSpringIntegrationBambooBitbucketJiraTest { +class ConductAgreementServiceTest extends AbstractSpringIntegrationBambooBitbucketJiraTest { private static final String TEST_PREFIX = "coursecodeofconductservice"; @@ -26,7 +26,7 @@ class CourseCodeOfConductAgreementServiceTest extends AbstractSpringIntegrationB private CourseRepository courseRepository; @Autowired - private CourseCodeOfConductAgreementService courseCodeOfConductAgreementService; + private ConductAgreementService conductAgreementService; @Autowired private UserUtilService userUtilService; @@ -41,15 +41,15 @@ void fetchAndAgreeAndResetCodeOfConductAgreement() { var user = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); var course = CourseFactory.generateCourse(null, PAST_TIMESTAMP, FUTURE_TIMESTAMP, new HashSet<>(), "student", "tutor", "editor", "instructor"); courseRepository.save(course); - var resultBeforeAgreement = courseCodeOfConductAgreementService.fetchUserAgreesToCodeOfConductInCourse(user, course); + var resultBeforeAgreement = conductAgreementService.fetchUserAgreesToCodeOfConductInCourse(user, course); assertThat(resultBeforeAgreement).isFalse(); - courseCodeOfConductAgreementService.setUserAgreesToCodeOfConductInCourse(user, course); - var resultAfterAgreement = courseCodeOfConductAgreementService.fetchUserAgreesToCodeOfConductInCourse(user, course); + conductAgreementService.setUserAgreesToCodeOfConductInCourse(user, course); + var resultAfterAgreement = conductAgreementService.fetchUserAgreesToCodeOfConductInCourse(user, course); assertThat(resultAfterAgreement).isTrue(); - courseCodeOfConductAgreementService.resetUsersAgreeToCodeOfConductInCourse(course); - var resultAfterReset = courseCodeOfConductAgreementService.fetchUserAgreesToCodeOfConductInCourse(user, course); + conductAgreementService.resetUsersAgreeToCodeOfConductInCourse(course); + var resultAfterReset = conductAgreementService.fetchUserAgreesToCodeOfConductInCourse(user, course); assertThat(resultAfterReset).isFalse(); } } From 034310c050727dadd5ee996bdde445c60a28cf07 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Fri, 6 Oct 2023 14:53:10 +0200 Subject: [PATCH 61/76] Add curly braces --- .../tum/in/www1/artemis/domain/ConductAgreement.java | 10 +++++----- .../tum/in/www1/artemis/domain/ConductAgreementId.java | 6 ++++-- .../{courseCodeOfConduct.json => codeOfConduct.json} | 0 .../{courseCodeOfConduct.json => codeOfConduct.json} | 0 .../de/tum/in/www1/artemis/FileIntegrationTest.java | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) rename src/main/webapp/i18n/de/{courseCodeOfConduct.json => codeOfConduct.json} (100%) rename src/main/webapp/i18n/en/{courseCodeOfConduct.json => codeOfConduct.json} (100%) diff --git a/src/main/java/de/tum/in/www1/artemis/domain/ConductAgreement.java b/src/main/java/de/tum/in/www1/artemis/domain/ConductAgreement.java index 7d3d608edda3..bc95a6c3a5e0 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/ConductAgreement.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/ConductAgreement.java @@ -43,12 +43,12 @@ public void setUser(User user) { @Override public boolean equals(Object o) { - if (this == o) + if (this == o) { return true; - if (o == null || getClass() != o.getClass()) - return false; - if (!super.equals(o)) + } + if (o == null || getClass() != o.getClass()) { return false; + } ConductAgreement that = (ConductAgreement) o; return course.equals(that.course) && user.equals(that.user); } @@ -60,6 +60,6 @@ public int hashCode() { @Override public String toString() { - return "CourseCodeOfConductAgreement{" + "course=" + course + ", user=" + user + '}'; + return "ConductAgreement{" + "course=" + course + ", user=" + user + '}'; } } diff --git a/src/main/java/de/tum/in/www1/artemis/domain/ConductAgreementId.java b/src/main/java/de/tum/in/www1/artemis/domain/ConductAgreementId.java index b233fb485954..930de471cea1 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/ConductAgreementId.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/ConductAgreementId.java @@ -39,10 +39,12 @@ public void setUser(Long user) { @Override public boolean equals(Object o) { - if (this == o) + if (this == o) { return true; - if (o == null || getClass() != o.getClass()) + } + if (o == null || getClass() != o.getClass()) { return false; + } ConductAgreementId that = (ConductAgreementId) o; return course.equals(that.course) && user.equals(that.user); } diff --git a/src/main/webapp/i18n/de/courseCodeOfConduct.json b/src/main/webapp/i18n/de/codeOfConduct.json similarity index 100% rename from src/main/webapp/i18n/de/courseCodeOfConduct.json rename to src/main/webapp/i18n/de/codeOfConduct.json diff --git a/src/main/webapp/i18n/en/courseCodeOfConduct.json b/src/main/webapp/i18n/en/codeOfConduct.json similarity index 100% rename from src/main/webapp/i18n/en/courseCodeOfConduct.json rename to src/main/webapp/i18n/en/codeOfConduct.json diff --git a/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java index dabbd409e0dd..ee0dbbd43010 100644 --- a/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/FileIntegrationTest.java @@ -145,7 +145,7 @@ void testGetCourseIcon() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") - void testGetCourseCodeOfConductTemplate() throws Exception { + void testGetCodeOfConductTemplate() throws Exception { var template = request.get("/api/files/templates/code-of-conduct", HttpStatus.OK, String.class); assertThat(template).startsWith(" - +
    From 0eeadcc130cdf4c93a4637ce0e1871a9685fffa1 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Tue, 10 Oct 2023 12:41:05 +0200 Subject: [PATCH 68/76] Update tests - https://bamboo.ase.in.tum.de/browse/ARTEMIS-TESTS5385-JAVATEST-32 --- .../java/de/tum/in/www1/artemis/course/CourseUtilService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/de/tum/in/www1/artemis/course/CourseUtilService.java b/src/test/java/de/tum/in/www1/artemis/course/CourseUtilService.java index cc68fa3725bf..f99eab1f055c 100644 --- a/src/test/java/de/tum/in/www1/artemis/course/CourseUtilService.java +++ b/src/test/java/de/tum/in/www1/artemis/course/CourseUtilService.java @@ -153,6 +153,7 @@ public Course createCourse(Long id) { public Course createCourseWithMessagingEnabled() { Course course = CourseFactory.generateCourse(null, pastTimestamp, futureTimestamp, new HashSet<>(), "tumuser", "tutor", "editor", "instructor", true); + course.setCourseInformationSharingMessagingCodeOfConduct("Code of Conduct"); return courseRepo.save(course); } From a0647b9cdc0ddb67b8fa13ebf991058c206c5ff7 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Tue, 10 Oct 2023 12:57:34 +0200 Subject: [PATCH 69/76] Add code of conduct - https://bamboo.ase.in.tum.de/browse/ARTEMIS-AETG2472-DA-30/test/case/677293600 --- .../cypress/support/requests/CourseManagementAPIRequests.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/cypress/support/requests/CourseManagementAPIRequests.ts b/src/test/cypress/support/requests/CourseManagementAPIRequests.ts index bc4104fb61f0..b13aa1d8201b 100644 --- a/src/test/cypress/support/requests/CourseManagementAPIRequests.ts +++ b/src/test/cypress/support/requests/CourseManagementAPIRequests.ts @@ -59,10 +59,12 @@ export class CourseManagementAPIRequests { if (allowCommunication && allowMessaging) { course.courseInformationSharingConfiguration = CourseInformationSharingConfiguration.COMMUNICATION_AND_MESSAGING; + course.courseInformationSharingMessagingCodeOfConduct = 'Code of Conduct'; } else if (allowCommunication) { course.courseInformationSharingConfiguration = CourseInformationSharingConfiguration.COMMUNICATION_ONLY; } else if (allowMessaging) { course.courseInformationSharingConfiguration = CourseInformationSharingConfiguration.MESSAGING_ONLY; + course.courseInformationSharingMessagingCodeOfConduct = 'Code of Conduct'; } else { course.courseInformationSharingConfiguration = CourseInformationSharingConfiguration.DISABLED; } From baf2307cad2b32a1efa67fbe689e6e3a0d8e3545 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Tue, 10 Oct 2023 13:37:23 +0200 Subject: [PATCH 70/76] Update test - https://bamboo.ase.in.tum.de/browse/ARTEMIS-TESTS5385-JAVATEST-34/test/case/684177906 --- .../in/www1/artemis/service/ConductAgreementServiceTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/de/tum/in/www1/artemis/service/ConductAgreementServiceTest.java b/src/test/java/de/tum/in/www1/artemis/service/ConductAgreementServiceTest.java index adcb4dfd4cfc..6508a108dee6 100644 --- a/src/test/java/de/tum/in/www1/artemis/service/ConductAgreementServiceTest.java +++ b/src/test/java/de/tum/in/www1/artemis/service/ConductAgreementServiceTest.java @@ -37,9 +37,10 @@ void init() { } @Test - void fetchAndAgreeAndResetCodeOfConductAgreement() { + void fetchAndAgreeAndResetConductAgreement() { var user = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); var course = CourseFactory.generateCourse(null, PAST_TIMESTAMP, FUTURE_TIMESTAMP, new HashSet<>(), "student", "tutor", "editor", "instructor"); + course.setCourseInformationSharingMessagingCodeOfConduct("Code of Conduct"); courseRepository.save(course); var resultBeforeAgreement = conductAgreementService.fetchUserAgreesToCodeOfConductInCourse(user, course); assertThat(resultBeforeAgreement).isFalse(); From 52756cc27298f6a30285236b2a4bebf7ad072c4f Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Tue, 10 Oct 2023 23:50:10 +0200 Subject: [PATCH 71/76] Test --- .../artemis/service/ConductAgreementService.java | 6 ++---- .../course-conversations.component.spec.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/tum/in/www1/artemis/service/ConductAgreementService.java b/src/main/java/de/tum/in/www1/artemis/service/ConductAgreementService.java index c16902537585..694b2a9c2531 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/ConductAgreementService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/ConductAgreementService.java @@ -27,10 +27,8 @@ public class ConductAgreementService { * @return if the user agreed to the course's code of conduct */ public boolean fetchUserAgreesToCodeOfConductInCourse(User user, Course course) { - if (course.getCourseInformationSharingMessagingCodeOfConduct() == null) { - return true; - } - if (course.getCourseInformationSharingMessagingCodeOfConduct().isEmpty()) { + var codeOfConduct = course.getCourseInformationSharingMessagingCodeOfConduct(); + if (codeOfConduct == null || codeOfConduct.isEmpty()) { return true; } return conductAgreementRepository.findByCourseIdAndUserId(course.getId(), user.getId()).isPresent(); diff --git a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts index a82f94c4e878..201fdf08fd11 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts @@ -114,6 +114,11 @@ examples.forEach((activeConversation) => { return () => {}; }, }); + Object.defineProperty(metisConversationService, 'acceptCodeOfConduct', { + get: () => { + return () => {}; + }, + }); Object.defineProperty(metisService, 'posts', { get: () => postsSubject.asObservable(), }); @@ -165,5 +170,12 @@ examples.forEach((activeConversation) => { replaceUrl: true, }); }); + + it('should accept code of conduct', () => { + const spy = jest.spyOn(component.metisConversationService, 'acceptCodeOfConduct'); + component.ngOnInit(); + component.acceptCodeOfConduct(); + expect(spy).toHaveBeenCalledOnce(); + }); }); }); From 1a507f9e8093708161c308e192115a4e068a2829 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 11 Oct 2023 19:23:21 +0200 Subject: [PATCH 72/76] Increase code coverage --- .../service/ConductAgreementServiceTest.java | 15 ++++++ .../course/course-update.component.spec.ts | 48 +++++++++++++++++++ ...sation-selection-sidebar.component.spec.ts | 6 +++ .../metis-conversation.service.spec.ts | 23 +++++++++ 4 files changed, 92 insertions(+) diff --git a/src/test/java/de/tum/in/www1/artemis/service/ConductAgreementServiceTest.java b/src/test/java/de/tum/in/www1/artemis/service/ConductAgreementServiceTest.java index 6508a108dee6..9324e363a4c4 100644 --- a/src/test/java/de/tum/in/www1/artemis/service/ConductAgreementServiceTest.java +++ b/src/test/java/de/tum/in/www1/artemis/service/ConductAgreementServiceTest.java @@ -36,6 +36,21 @@ void init() { userUtilService.addUsers(TEST_PREFIX, 1, 0, 0, 3); } + @Test + void fetchConductAgreementIfCodeOfConductIsNullOrEmpty() { + var user = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); + var course = CourseFactory.generateCourse(null, PAST_TIMESTAMP, FUTURE_TIMESTAMP, new HashSet<>(), "student", "tutor", "editor", "instructor"); + course.setCourseInformationSharingMessagingCodeOfConduct(null); + courseRepository.save(course); + var resultIfCodeOfConductIsNull = conductAgreementService.fetchUserAgreesToCodeOfConductInCourse(user, course); + assertThat(resultIfCodeOfConductIsNull).isTrue(); + + course.setCourseInformationSharingMessagingCodeOfConduct(""); + courseRepository.save(course); + var resultIfCodeOfConductIsEmpty = conductAgreementService.fetchUserAgreesToCodeOfConductInCourse(user, course); + assertThat(resultIfCodeOfConductIsEmpty).isTrue(); + } + @Test void fetchAndAgreeAndResetConductAgreement() { var user = userUtilService.getUserByLogin(TEST_PREFIX + "student1"); diff --git a/src/test/javascript/spec/component/course/course-update.component.spec.ts b/src/test/javascript/spec/component/course/course-update.component.spec.ts index 16a57870c0dd..fb24f0994644 100644 --- a/src/test/javascript/spec/component/course/course-update.component.spec.ts +++ b/src/test/javascript/spec/component/course/course-update.component.spec.ts @@ -688,3 +688,51 @@ describe('Course Management Update Component', () => { }); }); }); + +describe('Course Management Update Component Create', () => { + let component: CourseUpdateComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ArtemisTestModule, MockModule(ReactiveFormsModule), MockModule(FormsModule), ImageCropperModule, MockDirective(NgbTypeahead), MockModule(NgbTooltipModule)], + providers: [ + { provide: LocalStorageService, useClass: MockSyncStorage }, + { provide: SessionStorageService, useClass: MockSyncStorage }, + { provide: AccountService, useClass: MockAccountService }, + MockProvider(TranslateService), + MockProvider(LoadImageService), + ], + declarations: [ + CourseUpdateComponent, + MarkdownEditorStubComponent, + MockComponent(ColorSelectorComponent), + MockComponent(FormDateTimePickerComponent), + MockComponent(HelpIconComponent), + MockComponent(SecuredImageComponent), + MockDirective(FeatureToggleHideDirective), + MockDirective(HasAnyAuthorityDirective), + MockDirective(TranslateDirective), + MockPipe(ArtemisTranslatePipe), + MockPipe(RemoveKeysPipe), + ], + }) + .compileComponents() + .then(() => { + fixture = TestBed.createComponent(CourseUpdateComponent); + component = fixture.componentInstance; + }); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should get code of conduct template if a new course is created', () => { + const codeOfConduct = 'Code of Conduct'; + const httpStub = jest.spyOn(component.fileService.http, 'get').mockReturnValue(of(new HttpResponse({ body: codeOfConduct }))); + fixture.detectChanges(); + expect(httpStub).toHaveBeenCalledOnce(); + expect(component.course.courseInformationSharingMessagingCodeOfConduct).toEqual(codeOfConduct); + }); +}); diff --git a/src/test/javascript/spec/component/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.spec.ts index 7ad8de69f2b3..a1ba58b74f9f 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.spec.ts @@ -289,6 +289,12 @@ examples.forEach((activeConversation) => { expect(onConversationsUpdateSpy).toHaveBeenCalledWith(component.allConversations); })); + it('should open code of conduct', () => { + const metisSpy = jest.spyOn(component.metisConversationService, 'setCodeOfConduct'); + component.openCodeOfConduct(); + expect(metisSpy).toHaveBeenCalledOnce(); + }); + function createConversationDialogTest(modalReturnValue: any, dialog: Type, buttonId: string) { fixture.detectChanges(); tick(301); diff --git a/src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts index b74aaa8c7be4..b23d85dc438f 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts @@ -337,4 +337,27 @@ describe('MetisConversationService', () => { expect(numberOfSubscriptions).toBe(unreadMessages ? 1 : 0); }); + + it('should set code of conduct', () => { + metisConversationService.setCodeOfConduct(); + metisConversationService.isCodeOfConductPresented$.subscribe((isCodeOfConductPresented: boolean) => { + expect(isCodeOfConductPresented).toBeTrue(); + }); + }); + + it('should check and accept code of conduct', () => { + const checkStub = jest.spyOn(metisConversationService.conversationService, 'checkIsCodeOfConductAccepted').mockReturnValue(of(new HttpResponse({ body: false }))); + metisConversationService.checkIsCodeOfConductAccepted(course); + metisConversationService.isCodeOfConductAccepted$.subscribe((isCodeOfConductAccepted: boolean) => { + expect(isCodeOfConductAccepted).toBeFalse(); + }); + expect(checkStub).toHaveBeenCalledOnce(); + + const conversationServiceStub = jest.spyOn(metisConversationService.conversationService, 'acceptCodeOfConduct').mockReturnValue(of(new HttpResponse({}))); + metisConversationService.acceptCodeOfConduct(course); + metisConversationService.isCodeOfConductAccepted$.subscribe((isCodeOfConductAccepted: boolean) => { + expect(isCodeOfConductAccepted).toBeTrue(); + }); + expect(conversationServiceStub).toHaveBeenCalledOnce(); + }); }); From 7a577f06c3665f7c553b89dd5210ff745865db3d Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 11 Oct 2023 20:24:04 +0200 Subject: [PATCH 73/76] Fix private/protected access --- .../course/course-update.component.spec.ts | 19 +++++++++++++++---- .../metis-conversation.service.spec.ts | 4 ++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/test/javascript/spec/component/course/course-update.component.spec.ts b/src/test/javascript/spec/component/course/course-update.component.spec.ts index fb24f0994644..94bf2a3a17f5 100644 --- a/src/test/javascript/spec/component/course/course-update.component.spec.ts +++ b/src/test/javascript/spec/component/course/course-update.component.spec.ts @@ -37,6 +37,7 @@ import { By } from '@angular/platform-browser'; import { EventManager } from 'app/core/util/event-manager.service'; import { cloneDeep } from 'lodash-es'; import { FeatureToggleHideDirective } from 'app/shared/feature-toggle/feature-toggle-hide.directive'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; @Component({ selector: 'jhi-markdown-editor', template: '' }) class MarkdownEditorStubComponent { @@ -692,10 +693,19 @@ describe('Course Management Update Component', () => { describe('Course Management Update Component Create', () => { let component: CourseUpdateComponent; let fixture: ComponentFixture; + let httpMock: HttpTestingController; beforeEach(() => { TestBed.configureTestingModule({ - imports: [ArtemisTestModule, MockModule(ReactiveFormsModule), MockModule(FormsModule), ImageCropperModule, MockDirective(NgbTypeahead), MockModule(NgbTooltipModule)], + imports: [ + ArtemisTestModule, + HttpClientTestingModule, + MockModule(ReactiveFormsModule), + MockModule(FormsModule), + ImageCropperModule, + MockDirective(NgbTypeahead), + MockModule(NgbTooltipModule), + ], providers: [ { provide: LocalStorageService, useClass: MockSyncStorage }, { provide: SessionStorageService, useClass: MockSyncStorage }, @@ -721,6 +731,7 @@ describe('Course Management Update Component Create', () => { .then(() => { fixture = TestBed.createComponent(CourseUpdateComponent); component = fixture.componentInstance; + httpMock = TestBed.inject(HttpTestingController); }); }); @@ -729,10 +740,10 @@ describe('Course Management Update Component Create', () => { }); it('should get code of conduct template if a new course is created', () => { - const codeOfConduct = 'Code of Conduct'; - const httpStub = jest.spyOn(component.fileService.http, 'get').mockReturnValue(of(new HttpResponse({ body: codeOfConduct }))); fixture.detectChanges(); - expect(httpStub).toHaveBeenCalledOnce(); + const req = httpMock.expectOne({ method: 'GET' }); + const codeOfConduct = 'Code of Conduct'; + req.flush(codeOfConduct); expect(component.course.courseInformationSharingMessagingCodeOfConduct).toEqual(codeOfConduct); }); }); diff --git a/src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts index b23d85dc438f..212c965ff83c 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts @@ -346,14 +346,14 @@ describe('MetisConversationService', () => { }); it('should check and accept code of conduct', () => { - const checkStub = jest.spyOn(metisConversationService.conversationService, 'checkIsCodeOfConductAccepted').mockReturnValue(of(new HttpResponse({ body: false }))); + const checkStub = jest.spyOn(conversationService, 'checkIsCodeOfConductAccepted').mockReturnValue(of(new HttpResponse({ body: false }))); metisConversationService.checkIsCodeOfConductAccepted(course); metisConversationService.isCodeOfConductAccepted$.subscribe((isCodeOfConductAccepted: boolean) => { expect(isCodeOfConductAccepted).toBeFalse(); }); expect(checkStub).toHaveBeenCalledOnce(); - const conversationServiceStub = jest.spyOn(metisConversationService.conversationService, 'acceptCodeOfConduct').mockReturnValue(of(new HttpResponse({}))); + const conversationServiceStub = jest.spyOn(conversationService, 'acceptCodeOfConduct').mockReturnValue(of(new HttpResponse({}))); metisConversationService.acceptCodeOfConduct(course); metisConversationService.isCodeOfConductAccepted$.subscribe((isCodeOfConductAccepted: boolean) => { expect(isCodeOfConductAccepted).toBeTrue(); From 897d783ff8c73f73e5c08685726579a7bdf2165e Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 11 Oct 2023 21:05:44 +0200 Subject: [PATCH 74/76] Add time zone --- .../spec/component/course/course-update.component.spec.ts | 3 +++ .../services/metis-conversation.service.spec.ts | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/test/javascript/spec/component/course/course-update.component.spec.ts b/src/test/javascript/spec/component/course/course-update.component.spec.ts index 94bf2a3a17f5..ce6174d467de 100644 --- a/src/test/javascript/spec/component/course/course-update.component.spec.ts +++ b/src/test/javascript/spec/component/course/course-update.component.spec.ts @@ -691,6 +691,7 @@ describe('Course Management Update Component', () => { }); describe('Course Management Update Component Create', () => { + const validTimeZone = 'Europe/Berlin'; let component: CourseUpdateComponent; let fixture: ComponentFixture; let httpMock: HttpTestingController; @@ -729,6 +730,7 @@ describe('Course Management Update Component Create', () => { }) .compileComponents() .then(() => { + (Intl as any).supportedValuesOf = () => [validTimeZone]; fixture = TestBed.createComponent(CourseUpdateComponent); component = fixture.componentInstance; httpMock = TestBed.inject(HttpTestingController); @@ -737,6 +739,7 @@ describe('Course Management Update Component Create', () => { afterEach(() => { jest.restoreAllMocks(); + (Intl as any).supportedValuesOf = undefined; }); it('should get code of conduct template if a new course is created', () => { diff --git a/src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts index 212c965ff83c..872afc988f93 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/services/metis-conversation.service.spec.ts @@ -353,11 +353,11 @@ describe('MetisConversationService', () => { }); expect(checkStub).toHaveBeenCalledOnce(); - const conversationServiceStub = jest.spyOn(conversationService, 'acceptCodeOfConduct').mockReturnValue(of(new HttpResponse({}))); + const acceptStub = jest.spyOn(conversationService, 'acceptCodeOfConduct').mockReturnValue(of(new HttpResponse({}))); metisConversationService.acceptCodeOfConduct(course); metisConversationService.isCodeOfConductAccepted$.subscribe((isCodeOfConductAccepted: boolean) => { expect(isCodeOfConductAccepted).toBeTrue(); }); - expect(conversationServiceStub).toHaveBeenCalledOnce(); + expect(acceptStub).toHaveBeenCalledOnce(); }); }); From 5bf4860332ec2940b6e92f6747487a28e71feb1d Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Wed, 11 Oct 2023 23:15:48 +0200 Subject: [PATCH 75/76] Increase test coverage --- .../services/conversation.service.spec.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/test/javascript/spec/component/overview/course-conversations/services/conversation.service.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/services/conversation.service.spec.ts index 836c8e3052a6..5e4630802ed5 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/services/conversation.service.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/services/conversation.service.spec.ts @@ -85,6 +85,33 @@ describe('ConversationService', () => { tick(); })); + it('acceptCodeOfConduct', () => { + service + .acceptCodeOfConduct(1) + .pipe(take(1)) + .subscribe((res) => expect(res.body).toEqual({})); + const req = httpMock.expectOne({ method: 'PATCH' }); + req.flush({}); + }); + + it('checkIsCodeOfConductAccepted', () => { + service + .checkIsCodeOfConductAccepted(1) + .pipe(take(1)) + .subscribe((res) => expect(res.body).toBeTrue()); + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(true); + }); + + it('getResponsibleUsersForCodeOfConduct', () => { + service + .getResponsibleUsersForCodeOfConduct(1) + .pipe(take(1)) + .subscribe((res) => expect(res.body).toBeEmpty()); + const req = httpMock.expectOne({ method: 'GET' }); + req.flush([]); + }); + it('should return the correct conversation name', () => { const requestingUser = { id: 1, login: 'test', isRequestingUser: true } as ConversationUserDTO; const otherUser = { isRequestingUser: false, firstName: 'timo', lastName: 'moritz', login: 'login' } as ConversationUserDTO; From bddf9b2b93bfc0cb57ffab0102c5abe5a9318f50 Mon Sep 17 00:00:00 2001 From: Nityananda Zbil Date: Thu, 12 Oct 2023 00:03:03 +0200 Subject: [PATCH 76/76] On review --- .../course-conversations.component.spec.ts | 6 +++--- .../conversation-selection-sidebar.component.spec.ts | 2 +- .../mocks/service/mock-course-management.service.ts | 7 +------ 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts index 201fdf08fd11..b3a1428a71d1 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts @@ -172,10 +172,10 @@ examples.forEach((activeConversation) => { }); it('should accept code of conduct', () => { - const spy = jest.spyOn(component.metisConversationService, 'acceptCodeOfConduct'); - component.ngOnInit(); + const metisSpy = jest.spyOn(metisConversationService, 'acceptCodeOfConduct'); + fixture.detectChanges(); component.acceptCodeOfConduct(); - expect(spy).toHaveBeenCalledOnce(); + expect(metisSpy).toHaveBeenCalledOnce(); }); }); }); diff --git a/src/test/javascript/spec/component/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.spec.ts index a1ba58b74f9f..01af17f188db 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/layout/conversation-selection-sidebar/conversation-selection-sidebar.component.spec.ts @@ -290,7 +290,7 @@ examples.forEach((activeConversation) => { })); it('should open code of conduct', () => { - const metisSpy = jest.spyOn(component.metisConversationService, 'setCodeOfConduct'); + const metisSpy = jest.spyOn(metisConversationService, 'setCodeOfConduct'); component.openCodeOfConduct(); expect(metisSpy).toHaveBeenCalledOnce(); }); diff --git a/src/test/javascript/spec/helpers/mocks/service/mock-course-management.service.ts b/src/test/javascript/spec/helpers/mocks/service/mock-course-management.service.ts index a7f4b5efd75a..ee9a21194fd3 100644 --- a/src/test/javascript/spec/helpers/mocks/service/mock-course-management.service.ts +++ b/src/test/javascript/spec/helpers/mocks/service/mock-course-management.service.ts @@ -4,8 +4,7 @@ import { Course, CourseGroup } from 'app/entities/course.model'; import { TextExercise } from 'app/entities/text-exercise.model'; import { Exercise } from 'app/entities/exercise.model'; import { User } from '@sentry/angular-ivy'; -import { EntityArrayResponseType, RoleGroup } from 'app/course/manage/course-management.service'; -import { UserPublicInfoDTO } from 'app/core/user/user.model'; +import { EntityArrayResponseType } from 'app/course/manage/course-management.service'; export class MockCourseManagementService { mockExercises: Exercise[] = [new TextExercise(undefined, undefined)]; @@ -42,8 +41,4 @@ export class MockCourseManagementService { getAll(): Observable { return of(new HttpResponse({ body: [] })); } - - searchUsers(courseId: number, loginOrName: string, roles: RoleGroup[]): Observable> { - return of(new HttpResponse({ body: [] })); - } }