Skip to content

Commit 3517210

Browse files
committed
Merge branch 'develop' into chore/improve-quiz-submit
2 parents 88d9ff9 + 6513784 commit 3517210

193 files changed

Lines changed: 4885 additions & 3756 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ jobs:
238238

239239
client-tests:
240240
runs-on: aet-large-ubuntu
241-
timeout-minutes: 30
241+
timeout-minutes: 45
242242
steps:
243243
- uses: actions/checkout@v6
244244
# It seems like there is some memory issue with these tests with the project-wide default node option

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ Organized by feature module:
169169
- All new UI elements must be implemented using PrimeNG components
170170
- We are migrating from Bootstrap to PrimeNG; do not introduce new Bootstrap components
171171
- Existing Bootstrap usage will be migrated incrementally
172+
- **`@ng-bootstrap/ng-bootstrap` is deprecated** — do not use `NgbModal`, `NgbActiveModal`, `NgbModalRef`, `NgbTooltip`, `NgbDropdown`, etc. in new code. Use PrimeNG's `DialogService` (`primeng/dynamicdialog`) for modals, `p-tooltip` for tooltips, etc. ng-bootstrap is incompatible with Angular signal inputs (assigning to `modalRef.componentInstance.X` silently fails when `X` is `input()`/`input.required()`). Existing usages are being migrated.
172173

173174
### General
174175
- LF line endings

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ Refer to [Using JHipster in production](http://www.jhipster.tech/production) for
199199
The following command can automate the deployment to a server. The example shows the deployment to the main Artemis test server (which runs a virtual machine):
200200

201201
```shell
202-
./artemis-server-cli deploy username@artemis-test0.artemis.in.tum.de -w build/libs/Artemis-9.1.2.war
202+
./artemis-server-cli deploy username@artemis-test0.artemis.in.tum.de -w build/libs/Artemis-9.1.3.war
203203
```
204204

205205
## Architecture

build.gradle

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ envProperties.each { key, value ->
4040
}
4141

4242
group = "de.tum.cit.aet.artemis"
43-
version = "9.1.2"
43+
version = "9.1.3"
4444
description = "Interactive Learning with Individual Feedback"
4545

4646
java {
@@ -102,9 +102,9 @@ gitProperties {
102102
}
103103

104104
// Override Spring Boot BOM versions to patched releases addressing known CVEs
105-
// thymeleaf 3.1.4.RELEASE: GHSA-7rgv-g3jm-rvv4, GHSA-vjp2-6m4m-cv5q
105+
// thymeleaf 3.1.5.RELEASE: GHSA-7rgv-g3jm-rvv4, GHSA-vjp2-6m4m-cv5q, GHSA-c9ph-gxww-7744
106106
// tomcat 11.0.21: GHSA-h9fx-5mv3-gmh6, GHSA-6q54-28g2-vpgm, GHSA-55j7-cm8x-wvmh
107-
ext["thymeleaf.version"] = "3.1.4.RELEASE"
107+
ext["thymeleaf.version"] = "3.1.5.RELEASE"
108108
ext["tomcat.version"] = "11.0.21"
109109

110110
configurations {
@@ -198,11 +198,15 @@ dependencies {
198198

199199
// Required by Sharing Platform
200200
implementation "org.codeability:SharingPluginPlatformAPI:1.2.1"
201-
// Required by Spring cloud
202-
implementation "org.apache.httpcomponents.client5:httpclient5:5.6.1"
203-
// Pin the cache module to 5.6.1 so its transitive httpclient5 declaration is also patched (CVE-2026-40542 / GHSA-v468-qcjx-r72w);
204-
// otherwise Spring Boot's BOM and Shibboleth pull in httpclient5-cache:5.5.2, which declares httpclient5:5.5.2 transitively.
205-
implementation "org.apache.httpcomponents.client5:httpclient5-cache:5.6.1"
201+
// Required by Spring cloud.
202+
// Pinned to 5.5.1: not vulnerable to CVE-2026-40542 / GHSA-v468-qcjx-r72w (vulnerable range >= 5.6-alpha1, < 5.6.1).
203+
// The CVE affects the SCRAM-SHA-256 authentication scheme that was first introduced in 5.6; that code path does not
204+
// exist in 5.5.x, and Artemis does not use SCRAM-SHA-256 anywhere. A transitive request for httpclient5:5.6 from
205+
// io.weaviate:client6 (and httpclient5:5.5.2 from httpclient5-cache via the Spring BOM / Shibboleth) is resolved
206+
// down to 5.5.1 by this explicit pin.
207+
// We cannot upgrade to 5.6.1 (the patched release) because it breaks Hazelcast cluster formation; the bump was
208+
// reverted in PR #12652.
209+
implementation "org.apache.httpcomponents.client5:httpclient5:5.5.1"
206210
implementation "org.apache.httpcomponents.core5:httpcore5:5.3.6"
207211
implementation "org.apache.httpcomponents:httpmime:4.5.14"
208212

documentation/docs/developer/guidelines/client-development.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,16 @@ Use Angular's signal-based APIs instead. An ESLint rule (`enforce-signal-apis-in
6969

7070
**UI Component Library: PrimeNG**
7171

72+
<Callout variant={CalloutVariant.danger}>
73+
**`@ng-bootstrap/ng-bootstrap` is deprecated.** Do not use `NgbModal`, `NgbActiveModal`, `NgbModalRef`, `NgbTooltip`, `NgbDropdown`, or any other ng-bootstrap component in new code. ng-bootstrap is incompatible with Angular signal inputs (`input()`/`input.required()`) — assigning to a signal input via `modalRef.componentInstance.X = Y` silently fails. We are removing the dependency entirely; existing usages are being migrated to PrimeNG incrementally.
74+
</Callout>
75+
7276
<Callout variant={CalloutVariant.info}>
7377
**We are migrating from Bootstrap to PrimeNG.** All new UI elements must be implemented using [PrimeNG](https://primeng.org/) components. Do not introduce new Bootstrap components. Existing Bootstrap usage will be migrated incrementally.
7478
</Callout>
7579

7680
* Use PrimeNG components (buttons, tables, dialogs, inputs, etc.) for all new UI development.
81+
* For modals/dialogs, use PrimeNG's `DialogService` (`primeng/dynamicdialog`) — see `src/main/webapp/app/exam/manage/exam-management/exam-management.component.ts` for a reference implementation.
7782
* Refer to the PrimeNG documentation for available components and usage: https://primeng.org/
7883
* Bootstrap CSS utility classes (e.g. `d-flex`, `ms-2`) may still be used for layout until the migration is complete, but prefer PrimeNG's built-in layout capabilities where available.
7984

jest.config.js

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,17 @@ module.exports = {
109109
'!<rootDir>/src/main/webapp/app/shared/table-view/**', // table view module uses Vitest (see vitest.config.ts)
110110
'!<rootDir>/src/main/webapp/app/shared/components/buttons/**', // buttons module uses Vitest (see vitest.config.ts)
111111
'!<rootDir>/src/main/webapp/app/exam/manage/students/**', // exam manage students module uses Vitest (see vitest.config.ts)
112+
'!<rootDir>/src/main/webapp/app/exam/manage/student-exams/**', // exam manage student-exams module uses Vitest (see vitest.config.ts)
113+
'!<rootDir>/src/main/webapp/app/exam/manage/test-runs/**', // exam manage test-runs module uses Vitest (see vitest.config.ts)
114+
'!<rootDir>/src/main/webapp/app/exam/manage/exercise-groups/**', // exam manage exercise groups module uses Vitest (see vitest.config.ts)
115+
'!<rootDir>/src/main/webapp/app/exam/manage/suspicious-behavior/**', // exam manage suspicious behavior module uses Vitest (see vitest.config.ts)
116+
'!<rootDir>/src/main/webapp/app/exam/manage/services/**', // exam manage services module uses Vitest (see vitest.config.ts)
117+
'!<rootDir>/src/main/webapp/app/exam/manage/exam-management/**', // exam management module uses Vitest (see vitest.config.ts)
118+
'!<rootDir>/src/main/webapp/app/exam/manage/exam-scores/**', // exam scores module uses Vitest (see vitest.config.ts)
119+
'!<rootDir>/src/main/webapp/app/exam/manage/exam-status/**', // exam status module uses Vitest (see vitest.config.ts)
120+
'!<rootDir>/src/main/webapp/app/exam/manage/exams/**', // exam manage exams (detail/import/update/checklist/mode-picker) uses Vitest (see vitest.config.ts)
121+
'!<rootDir>/src/main/webapp/app/exam/shared/**', // exam shared module uses Vitest (see vitest.config.ts)
122+
'!<rootDir>/src/main/webapp/app/exam/overview/**', // exam overview module uses Vitest (see vitest.config.ts)
112123
'!<rootDir>/src/main/webapp/app/shared/feature-toggle/**', // feature-toggle service uses Vitest (see vitest.config.ts)
113124
'!<rootDir>/src/main/webapp/app/shared/sort/**', // sort directives use vitest (see vitest.config.ts)
114125
'!<rootDir>/src/main/webapp/app/shared/user-import/util/**', // user import utils use Vitest (see vitest.config.ts)
@@ -142,6 +153,17 @@ module.exports = {
142153
'<rootDir>/src/main/webapp/app/atlas/', // atlas module uses Vitest
143154
'<rootDir>/src/main/webapp/app/shared/components/buttons/', // buttons module uses Vitest
144155
'<rootDir>/src/main/webapp/app/exam/manage/students/', // exam manage students module uses Vitest
156+
'<rootDir>/src/main/webapp/app/exam/manage/student-exams/', // exam manage student-exams module uses Vitest
157+
'<rootDir>/src/main/webapp/app/exam/manage/test-runs/', // exam manage test-runs module uses Vitest
158+
'<rootDir>/src/main/webapp/app/exam/manage/exercise-groups/', // exam manage exercise groups module uses Vitest
159+
'<rootDir>/src/main/webapp/app/exam/manage/suspicious-behavior/', // exam manage suspicious behavior module uses Vitest
160+
'<rootDir>/src/main/webapp/app/exam/manage/services/', // exam manage services module uses Vitest
161+
'<rootDir>/src/main/webapp/app/exam/manage/exam-management/', // exam management module uses Vitest
162+
'<rootDir>/src/main/webapp/app/exam/manage/exam-scores/', // exam scores module uses Vitest
163+
'<rootDir>/src/main/webapp/app/exam/manage/exam-status/', // exam status module uses Vitest
164+
'<rootDir>/src/main/webapp/app/exam/manage/exams/', // exam manage exams uses Vitest
165+
'<rootDir>/src/main/webapp/app/exam/shared/', // exam shared module uses Vitest
166+
'<rootDir>/src/main/webapp/app/exam/overview/', // exam overview module uses Vitest
145167
'<rootDir>/src/main/webapp/app/shared/sort/', // sort directives use Vitest
146168
'<rootDir>/src/main/webapp/app/shared/user-import/util/', // user import utils use Vitest
147169
'<rootDir>/src/main/webapp/app/shared/table-view/', // table view module uses Vitest
@@ -159,12 +181,15 @@ module.exports = {
159181
],
160182
// Global coverage thresholds for Jest. Modules using Vitest (e.g., fileupload) have their own
161183
// coverage thresholds in vitest.config.ts. Per-module thresholds are enforced by check-client-module-coverage.mjs
184+
// Lowered with the exam-module Vitest migration: when ~100 spec files moved from Jest to Vitest,
185+
// a few peripheral helpers stopped being touched by the Jest run, dropping each global metric by 1-2pp.
186+
// Per-file coverage is unchanged — the exam-module tests still cover the same files, just under Vitest.
162187
coverageThreshold: {
163188
global: {
164-
statements: 85.5,
165-
branches: 74.1,
166-
functions: 75.4,
167-
lines: 86.5,
189+
statements: 83.5,
190+
branches: 73,
191+
functions: 73,
192+
lines: 84.5,
168193
},
169194
},
170195
// 'json-summary' reporter is used by supporting_scripts/code-coverage/module-coverage-client/check-client-module-coverage.mjs
@@ -201,6 +226,17 @@ module.exports = {
201226
'<rootDir>/src/main/webapp/app/tutorialgroup/', // tutorialgroup module
202227
'<rootDir>/src/main/webapp/app/atlas/', // atlas module
203228
'<rootDir>/src/main/webapp/app/exam/manage/students/', // exam manage students module
229+
'<rootDir>/src/main/webapp/app/exam/manage/student-exams/', // exam manage student-exams module
230+
'<rootDir>/src/main/webapp/app/exam/manage/test-runs/', // exam manage test-runs module
231+
'<rootDir>/src/main/webapp/app/exam/manage/exercise-groups/', // exam manage exercise groups module
232+
'<rootDir>/src/main/webapp/app/exam/manage/suspicious-behavior/', // exam manage suspicious behavior module
233+
'<rootDir>/src/main/webapp/app/exam/manage/services/', // exam manage services module
234+
'<rootDir>/src/main/webapp/app/exam/manage/exam-management/', // exam management module (vitest)
235+
'<rootDir>/src/main/webapp/app/exam/manage/exam-scores/', // exam scores module (vitest)
236+
'<rootDir>/src/main/webapp/app/exam/manage/exam-status/', // exam status module (vitest)
237+
'<rootDir>/src/main/webapp/app/exam/manage/exams/', // exam manage exams (detail/import/update/checklist/mode-picker) (vitest)
238+
'<rootDir>/src/main/webapp/app/exam/shared/', // exam shared module (vitest)
239+
'<rootDir>/src/main/webapp/app/exam/overview/', // exam overview module (vitest)
204240
'<rootDir>/src/main/webapp/app/shared/components/buttons/', // shared/buttons components
205241
'<rootDir>/src/main/webapp/app/shared/table-view/', // shared/table-view component
206242
'<rootDir>/src/main/webapp/app/shared/feature-toggle/', // feature-toggle service (vitest)

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "artemis",
3-
"version": "9.1.2",
3+
"version": "9.1.3",
44
"description": "Interactive Learning with Individual Feedback",
55
"private": true,
66
"license": "MIT",

src/main/java/de/tum/cit/aet/artemis/lecture/web/LectureResource.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,11 +220,16 @@ public ResponseEntity<SimpleLectureDTO> updateLecture(@RequestBody SimpleLecture
220220
if (updatedLectureDto.id() == null) {
221221
throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idNull");
222222
}
223-
Course course = courseRepository.findByIdElseThrow(updatedLectureDto.course.id());
224-
authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, course, null);
225-
226223
// This explicitly does NOT change any relationships such as attachments or lecture units
227224
Lecture originalLecture = lectureRepository.findByIdElseThrow(updatedLectureDto.id());
225+
Course course = originalLecture.getCourse();
226+
if (course == null) {
227+
throw new BadRequestAlertException("Lecture is not part of a course", ENTITY_NAME, "courseMissing");
228+
}
229+
authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, course, null);
230+
if (updatedLectureDto.course == null || !course.getId().equals(updatedLectureDto.course.id())) {
231+
throw new BadRequestAlertException("Lecture does not belong to the specified course", ENTITY_NAME, "courseMismatch");
232+
}
228233
updateLectureAttributesFromDTO(originalLecture, updatedLectureDto);
229234

230235
channelService.updateLectureChannel(originalLecture, updatedLectureDto.channelName());

src/main/webapp/app/core/course/overview/course-overview/course-overview.component.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,11 @@ export class CourseOverviewComponent extends BaseCourseContainerComponent implem
115115

116116
async ngOnInit() {
117117
this.toggleSidebarEventSubscription = this.courseSidebarService.toggleSidebar$.subscribe(() => {
118-
this.isSidebarCollapsed.update((value) => this.activatedComponentReference()?.isCollapsed ?? !value);
118+
this.isSidebarCollapsed.update((value) => {
119+
const componentRef = this.activatedComponentReference();
120+
const componentCollapsed = typeof componentRef?.isCollapsed === 'function' ? componentRef.isCollapsed() : (componentRef?.isCollapsed as boolean | undefined);
121+
return componentCollapsed ?? !value;
122+
});
119123
});
120124

121125
this.subscription = this.route?.params.subscribe(async (params: { courseId: string }) => {
@@ -149,7 +153,9 @@ export class CourseOverviewComponent extends BaseCourseContainerComponent implem
149153
});
150154

151155
this.courseActionItems.set(this.getCourseActionItems());
152-
this.isSidebarCollapsed.set(this.activatedComponentReference()?.isCollapsed ?? false);
156+
const componentRef = this.activatedComponentReference();
157+
const componentCollapsed = typeof componentRef?.isCollapsed === 'function' ? componentRef.isCollapsed() : (componentRef?.isCollapsed as boolean | undefined);
158+
this.isSidebarCollapsed.set(componentCollapsed ?? false);
153159
this.sidebarItems.set(this.getSidebarItems());
154160
await this.initAfterCourseLoad();
155161
}
@@ -293,7 +299,8 @@ export class CourseOverviewComponent extends BaseCourseContainerComponent implem
293299
}
294300
const childRouteComponent = this.activatedComponentReference();
295301
childRouteComponent?.toggleSidebar();
296-
this.isSidebarCollapsed.set(childRouteComponent!.isCollapsed);
302+
const componentCollapsed = typeof childRouteComponent!.isCollapsed === 'function' ? childRouteComponent!.isCollapsed() : (childRouteComponent!.isCollapsed as boolean);
303+
this.isSidebarCollapsed.set(componentCollapsed);
297304
}
298305

299306
getShowRefreshButton(): void {

0 commit comments

Comments
 (0)