Skip to content

Commit

Permalink
Merge branch 'develop' into pe/update-dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
Strohgelaender authored May 2, 2024
2 parents 4b3b2ce + 15f2350 commit 2a1c43d
Show file tree
Hide file tree
Showing 18 changed files with 946 additions and 29 deletions.
25 changes: 19 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import com.diffplug.spotless.FormatterFunc
import com.diffplug.spotless.FormatterStep
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent

import static com.diffplug.spotless.Formatter.NO_FILE_SENTINEL

buildscript {
repositories {
mavenLocal()
Expand Down Expand Up @@ -84,13 +88,22 @@ spotless {
removeUnusedImports()
trimTrailingWhitespace()

custom 'Refuse wildcard imports', {
// Wildcard imports can't be resolved by spotless itself.
// This will require the developer themselves to adhere to best practices.
if (it =~ /\nimport .*\*;/) {
throw new AssertionError("Do not use wildcard imports. 'spotlessApply' cannot resolve this issue. The following class vioaltes this rule:\n" + it)
// Wildcard imports can't be resolved by spotless itself.
// This will require the developer themselves to adhere to best practices.
addStep(FormatterStep.createNeverUpToDate("Refuse wildcard imports", new FormatterFunc() {
@Override
String apply(String s) throws Exception {
apply(s, NO_FILE_SENTINEL)
}
}

@Override
String apply(String s, File file) throws Exception {
if (s =~ /\nimport .*\*;/) {
throw new AssertionError("Do not use wildcard imports. 'spotlessApply' cannot resolve this issue.\n" +
"The following file violates this rule: " + file.getName())
}
}
}))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
* This includes a Docker client and an executor service that manages the queue of build jobs.
*/
@Configuration
@Profile({ "localci", PROFILE_BUILDAGENT })
@Profile(PROFILE_BUILDAGENT)
public class LocalCIConfiguration {

private final ProgrammingLanguageConfiguration programmingLanguageConfiguration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ public void populateBuildJobContainer(String buildJobContainerId, Path assignmen
for (int i = 0; i < auxiliaryRepositoriesPaths.length; i++) {
addAndPrepareDirectory(buildJobContainerId, auxiliaryRepositoriesPaths[i], LOCALCI_WORKING_DIRECTORY + "/testing-dir/" + auxiliaryRepositoryCheckoutDirectories[i]);
}
convertDosFilesToUnix(LOCALCI_WORKING_DIRECTORY + "/testing-dir/", buildJobContainerId);
// TODO: this might lead to issues in certain builds
// convertDosFilesToUnix(LOCALCI_WORKING_DIRECTORY + "/testing-dir/", buildJobContainerId);

createScriptFile(buildJobContainerId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void handleAuxiliaryRepositoriesWhenUpdatingExercises(ProgrammingExercise
return true;
}
AuxiliaryRepository auxiliaryRepositoryBeforeUpdate = auxiliaryRepositoryRepository.findById(repo.getId())
.orElseThrow(() -> new IllegalStateException("Edited an existing repository that is not in the data base!"));
.orElseThrow(() -> new IllegalStateException("Edited an existing repository that is not in the database!"));
return !repo.containsEqualStringValues(auxiliaryRepositoryBeforeUpdate);
}).toList());
validateAndUpdateExistingAuxiliaryRepositoriesOfProgrammingExercise(programmingExercise, newOrEditedAuxiliaryRepositories, updatedExercise);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,75 @@
<jhi-loading-indicator-container [isLoading]="isLoading">
<div class="input-group" [hidden]="!isCodeOfConductAccepted">
<!-- search bar -->
<input
name="searchText"
id="search"
[(ngModel)]="courseWideSearchTerm"
class="form-control"
(keyup.enter)="onSearch()"
placeholder="{{ 'artemisApp.metis.overview.searchBarDefault' | artemisTranslate }}"
/>
<jhi-button [btnType]="ButtonType.SECONDARY" [icon]="faTimes" (onClick)="courseWideSearchTerm = ''" />
<jhi-button id="search-submit" class="ms-1" [icon]="faSearch" (onClick)="onSearch()" />
<jhi-documentation-button [type]="documentationType" class="information-button" />
</div>
@if (formGroup && isCodeOfConductAccepted) {
<form [formGroup]="formGroup">
<!-- filter and sort controls -->
<div class="row mx-1 my-2 justify-content-start">
<!-- sort dropdown -->
<div class="col-12 col-sm-5 col-md-auto order-lg-2" style="margin-left: 80px">
<div class="row align-items-baseline justify-content-center">
<div class="col-auto p-0">{{ 'artemisApp.metis.overview.sortedByDate' | artemisTranslate }}</div>
<div class="col-auto p-0 clickable" role="button" (click)="onChangeSortDir()">
<fa-icon
[icon]="sortingOrder === SortDirection.ASCENDING ? faLongArrowAltUp : faLongArrowAltDown"
[ngbTooltip]="
sortingOrder === SortDirection.ASCENDING
? ('artemisApp.metis.overview.sortAscending' | artemisTranslate)
: ('artemisApp.metis.overview.sortDescending' | artemisTranslate)
"
/>
</div>
</div>
</div>
<!-- attribute filter -->
<div class="col-12 col-md-auto order-lg-1 d-flex align-items-center">
<div class="row selection-prefix justify-content-center">
<div class="col-auto p-0">
<fa-icon [icon]="faFilter" />
</div>
<div class="col-auto p-0 ps-2">
<input
class="form-check-input"
type="checkbox"
formControlName="filterToUnresolved"
name="filterToUnresolved"
id="filterToUnresolved"
(change)="onSelectContext()"
/>
<label for="filterToUnresolved" class="p-0">{{ 'artemisApp.metis.overview.filterToUnresolved' | artemisTranslate }}</label>
</div>
<div class="col-auto p-0 ps-2">
<input class="form-check-input" type="checkbox" formControlName="filterToOwn" name="filterToOwn" id="filterToOwn" (change)="onSelectContext()" />
<label for="filterToOwn" class="p-0">{{ 'artemisApp.metis.overview.filterToOwn' | artemisTranslate }}</label>
</div>
<div class="col-auto p-0 ps-2">
<input
class="form-check-input"
type="checkbox"
formControlName="filterToAnsweredOrReacted"
name="filterToAnsweredOrReacted"
id="filterToAnsweredOrReacted"
(change)="onSelectContext()"
/>
<label for="filterToAnsweredOrReacted" class="p-0">{{ 'artemisApp.metis.overview.filterToAnsweredOrReacted' | artemisTranslate }}</label>
</div>
</div>
</div>
</div>
</form>
}
<!-- only display after isCodeOfConductAccepted is loaded and set to false -->
@if (course && isCodeOfConductAccepted === false) {
<div>
Expand All @@ -18,24 +89,24 @@
<jhi-course-conversations-code-of-conduct [course]="course!" />
</div>
}
<div class="col pe-0 flex-grow-1" [ngClass]="{ 'card-border': activeConversation }" style="min-width: 200px">
<div class="col pe-0 flex-grow-1" [ngClass]="{ 'card-border': true }" style="min-width: 200px">
@if (activeConversation) {
<jhi-conversation-header />
<jhi-conversation-messages (openThread)="postInThread = $event" [course]="course" />
} @else {
<jhi-course-wide-search (openThread)="postInThread = $event" [courseWideSearchConfig]="courseWideSearchConfig" />
}
</div>
<div class="col d-flex flex-grow-1 justify-end px-0" style="max-width: min-content">
@if (!!postInThread) {
<jhi-conversation-thread-sidebar
[activeConversation]="postInThread.conversation!"
[readOnlyMode]="!!getAsChannel(postInThread.conversation)?.isArchived"
[activePost]="postInThread"
(closePostThread)="postInThread = undefined"
/>
}
</div>
@if (activeConversation) {
<div class="col d-flex flex-grow-1 justify-end px-0" style="max-width: min-content">
@if (!!postInThread) {
<jhi-conversation-thread-sidebar
[activeConversation]="activeConversation"
[readOnlyMode]="!!getAsChannel(activeConversation)?.isArchived"
[activePost]="postInThread"
(closePostThread)="postInThread = undefined"
/>
}
</div>
}
</div>
}
</jhi-loading-indicator-container>
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { Component, OnDestroy, OnInit, 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';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Subject, take, takeUntil } from 'rxjs';
import { MetisConversationService } from 'app/shared/metis/metis-conversation.service';
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 { PageType, SortDirection } from 'app/shared/metis/metis.util';
import { faFilter, faLongArrowAltDown, faLongArrowAltUp, faPlus, faSearch, faTimes } from '@fortawesome/free-solid-svg-icons';
import { ButtonType } from 'app/shared/components/button.component';
import { DocumentationType } from 'app/shared/components/documentation-button/documentation-button.component';
import { CourseWideSearchComponent, CourseWideSearchConfig } from 'app/overview/course-conversations/course-wide-search/course-wide-search.component';

@Component({
selector: 'jhi-course-conversations',
Expand All @@ -29,12 +34,32 @@ export class CourseConversationsComponent implements OnInit, OnDestroy {
isCodeOfConductAccepted?: boolean;
isCodeOfConductPresented: boolean = false;

@ViewChild(CourseWideSearchComponent)
courseWideSearch: CourseWideSearchComponent;

courseWideSearchConfig: CourseWideSearchConfig;
courseWideSearchTerm = '';
formGroup: FormGroup;
readonly documentationType: DocumentationType = 'Communications';
readonly ButtonType = ButtonType;
readonly SortDirection = SortDirection;
sortingOrder = SortDirection.ASCENDING;

// Icons
faPlus = faPlus;
faTimes = faTimes;
faFilter = faFilter;
faSearch = faSearch;
faLongArrowAltUp = faLongArrowAltUp;
faLongArrowAltDown = faLongArrowAltDown;

// MetisConversationService is created in course overview, so we can use it here
constructor(
private router: Router,
private activatedRoute: ActivatedRoute,
private metisConversationService: MetisConversationService,
private metisService: MetisService,
private formBuilder: FormBuilder,
) {}

getAsChannel = getAsChannelDTO;
Expand All @@ -57,6 +82,8 @@ export class CourseConversationsComponent implements OnInit, OnDestroy {
this.metisConversationService.isServiceSetup$.pipe(takeUntil(this.ngUnsubscribe)).subscribe((isServiceSetUp: boolean) => {
if (isServiceSetUp) {
this.course = this.metisConversationService.course;
this.initializeCourseWideSearchConfig();
this.resetFormGroup();
this.setupMetis();
this.subscribeToMetis();
this.subscribeToQueryParameter();
Expand Down Expand Up @@ -137,4 +164,42 @@ export class CourseConversationsComponent implements OnInit, OnDestroy {
this.metisConversationService.acceptCodeOfConduct(this.course);
}
}

initializeCourseWideSearchConfig() {
this.courseWideSearchConfig = new CourseWideSearchConfig();
this.courseWideSearchConfig.searchTerm = '';
this.courseWideSearchConfig.filterToUnresolved = false;
this.courseWideSearchConfig.filterToOwn = false;
this.courseWideSearchConfig.filterToAnsweredOrReacted = false;
this.courseWideSearchConfig.sortingOrder = SortDirection.ASCENDING;
}

onSearch() {
this.activeConversation = undefined;
this.courseWideSearchConfig.searchTerm = this.courseWideSearchTerm;
this.courseWideSearch?.onSearch();
}

onSelectContext(): void {
this.courseWideSearchConfig.filterToUnresolved = this.formGroup.get('filterToUnresolved')?.value;
this.courseWideSearchConfig.filterToOwn = this.formGroup.get('filterToOwn')?.value;
this.courseWideSearchConfig.filterToAnsweredOrReacted = this.formGroup.get('filterToAnsweredOrReacted')?.value;
this.courseWideSearchConfig.sortingOrder = this.sortingOrder;
if (!this.activeConversation) {
this.onSearch();
}
}

onChangeSortDir(): void {
this.sortingOrder = this.sortingOrder === SortDirection.DESCENDING ? SortDirection.ASCENDING : SortDirection.DESCENDING;
this.onSelectContext();
}

resetFormGroup(): void {
this.formGroup = this.formBuilder.group({
filterToUnresolved: false,
filterToOwn: false,
filterToAnsweredOrReacted: false,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { GroupChatCreateDialogComponent } from './dialogs/group-chat-create-dial
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';
import { CourseWideSearchComponent } from 'app/overview/course-conversations/course-wide-search/course-wide-search.component';

const routes: Routes = [
{
Expand Down Expand Up @@ -81,6 +82,7 @@ const routes: Routes = [
OneToOneChatCreateDialogComponent,
GroupChatCreateDialogComponent,
GroupChatIconComponent,
CourseWideSearchComponent,
],
})
export class CourseConversationsModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<div class="conversation-header">
<div class="d-flex justify-content-between">
<div>
@if (!courseWideSearchConfig.searchTerm) {
<h3 class="conversation-name d-inline-block rounded p-2 info">All Messages</h3>
} @else {
<h3 class="conversation-name d-inline-block rounded p-2 info">Search Results for "{{ courseWideSearchConfig.searchTerm }}"</h3>
}
</div>
</div>
</div>

<div class="row p-3 justify-content-center coursewide-search">
<div class="justify-content-center">
<div class="row mt-3">
<!-- loading messages -->
@if (isFetchingPosts) {
<div class="envelope">
<fa-icon size="3x" [icon]="faCircleNotch" [spin]="true" />
</div>
}
<!-- no message exist -->
@if (!isFetchingPosts && totalNumberOfPosts === 0) {
<div class="envelope">
<fa-icon size="5x" [icon]="faEnvelope" />
</div>
}
<!-- list of messages -->
<div
id="scrollableDiv"
#container
class="{{ totalNumberOfPosts !== 0 ? 'posting-infinite-scroll-container' : '' }}"
infinite-scroll
[scrollWindow]="false"
(scrolledUp)="fetchNextPage()"
>
<!-- list of all top level posts -->
<!-- answers are opened in the thread sidebar -->
@for (post of posts; track postsTrackByFn($index, post)) {
<jhi-posting-thread
#postingThread
[lastReadDate]="post.conversation?.lastMessageDate"
[hasChannelModerationRights]="!!getAsChannel(post.conversation)?.hasChannelModerationRights"
[id]="'item-' + post.id"
[post]="post"
[showAnswers]="false"
[readOnlyMode]="!!getAsChannel(post.conversation)?.isArchived"
[isCourseMessagesPage]="true"
[isCommunicationPage]="true"
(openThread)="setPostForThread($event)"
/>
}
</div>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.coursewide-search {
.bold-line {
height: 1px;
background: var(--metis-light-gray);
}

.form-select {
border: 0;
box-shadow: none !important;
}

.selection-prefix {
padding-top: 0.375rem !important;
}

.posting-infinite-scroll-container {
max-height: 700px;
overflow-y: auto;
}

.envelope {
text-align: center;
opacity: 0.75;
height: 250px;
padding-top: 100px;
padding-bottom: 100px;
}
}
Loading

0 comments on commit 2a1c43d

Please sign in to comment.