Skip to content
This repository was archived by the owner on Sep 10, 2025. It is now read-only.

Commit a055818

Browse files
committed
Merge remote-tracking branch 'origin/main'
2 parents 5fa0ecc + 5deb98d commit a055818

40 files changed

Lines changed: 626 additions & 617 deletions

EEDU-Backend/src/main/java/de/gaz/eedu/DataLoader.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,6 @@ private void createDefaultGroup()
142142
"root", // login id
143143
AccountType.ADMINISTRATOR,
144144
true, // enabled
145-
false, // locked
146-
UserStatus.PROSPECTIVE,
147145
themeEntity.getId(),
148146
new String[] { } // groups
149147
))).getFirst());

EEDU-Backend/src/main/java/de/gaz/eedu/course/CourseController.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import de.gaz.eedu.course.model.CourseCreateModel;
44
import de.gaz.eedu.course.model.CourseModel;
55
import de.gaz.eedu.entity.EntityController;
6-
import de.gaz.eedu.user.UserEntity;
76
import de.gaz.eedu.user.model.ReducedUserModel;
87
import lombok.AccessLevel;
98
import lombok.Getter;
@@ -76,7 +75,7 @@ public class CourseController extends EntityController<Long, CourseService, Cour
7675
return ResponseEntity.ok(getService().getCourses(user));
7776
}
7877

79-
@GetMapping("/get/courses")
78+
@GetMapping("/get")
8079
public @NotNull ResponseEntity<CourseModel[]> getOwnCourses(@AuthenticationPrincipal long user)
8180
{
8281
return getCourses(user);

EEDU-Backend/src/main/java/de/gaz/eedu/course/CourseEntity.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import de.gaz.eedu.user.model.ReducedUserModel;
1818
import jakarta.persistence.*;
1919
import lombok.*;
20+
import org.hibernate.annotations.Cascade;
2021
import org.jetbrains.annotations.Contract;
2122
import org.jetbrains.annotations.NotNull;
2223
import org.jetbrains.annotations.Nullable;
@@ -49,9 +50,9 @@ public class CourseEntity implements EntityModelRelation<Long, CourseModel>
4950
inverseJoinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id")
5051
)
5152
private final Set<UserEntity> users = new HashSet<>();
52-
@OneToMany(mappedBy = "course", orphanRemoval = true) @JsonManagedReference @Getter(AccessLevel.NONE)
53+
@OneToMany(mappedBy = "course", orphanRemoval = true) @JsonManagedReference @Getter(AccessLevel.NONE) @Cascade(org.hibernate.annotations.CascadeType.ALL)
5354
private final Set<FrequentAppointmentEntity> frequentAppointments = new HashSet<>();
54-
@OneToMany(mappedBy = "course") @JsonManagedReference @Getter(AccessLevel.NONE)
55+
@OneToMany(mappedBy = "course") @JsonManagedReference @Getter(AccessLevel.NONE) @Cascade(org.hibernate.annotations.CascadeType.ALL)
5556
private final Set<AppointmentEntryEntity> appointments = new HashSet<>();
5657
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Setter(AccessLevel.NONE) private Long id; // ID is final
5758
private String name;

EEDU-Backend/src/main/java/de/gaz/eedu/user/UserController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,14 +216,14 @@ public void logout(@AuthenticationPrincipal @Nullable Long user, @NotNull HttpSe
216216
log.info("User {} has been logged out.", user);
217217
}
218218

219-
@GetMapping("/all")
219+
@GetMapping("/get/all")
220220
@PreAuthorize("hasAuthority(T(de.gaz.eedu.user.privileges.SystemPrivileges).USER_OTHERS_GET.toString())")
221221
@Override public @NotNull ResponseEntity<Set<UserModel>> fetchAll()
222222
{
223223
return super.fetchAll();
224224
}
225225

226-
@PreAuthorize("isAuthenticated()") @GetMapping("/all/reduced")
226+
@PreAuthorize("@verificationService.isFullyAuthenticated()") @GetMapping("/all/reduced")
227227
public @NotNull ResponseEntity<ReducedUserModel[]> fetchAllReduced()
228228
{
229229
return ResponseEntity.ok(getService().findAllReduced().toArray(new ReducedUserModel[0]));

EEDU-Backend/src/main/java/de/gaz/eedu/user/model/UserCreateModel.java

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ public record UserCreateModel(
1616
@NotNull String loginName,
1717
@NotNull AccountType accountType,
1818
@NotNull Boolean enabled,
19-
@NotNull Boolean locked,
20-
@NotNull UserStatus status,
2119
@NotNull Long theme,
2220
@NotNull String[] groups
2321
) implements CreationModel<Long, UserEntity> {
@@ -29,9 +27,8 @@ public record UserCreateModel(
2927
userEntity.setLoginName(loginName());
3028
userEntity.setAccountType(accountType());
3129
userEntity.setEnabled(enabled());
32-
userEntity.setLocked(locked());
33-
userEntity.setStatus(status());
34-
userEntity.setSystemAccount(loginName().equals("root"));
30+
userEntity.setStatus(UserStatus.PROSPECTIVE);
31+
userEntity.setSystemAccount(Objects.equals(loginName(), "root"));
3532
return userEntity;
3633
}
3734

@@ -43,8 +40,6 @@ public record UserCreateModel(
4340
", loginName='" + loginName + '\'' +
4441
", accountType=" + accountType +
4542
", enabled=" + enabled +
46-
", locked=" + locked +
47-
", status=" + status +
4843
", theme=" + theme +
4944
", groups=" + Arrays.toString(groups) +
5045
'}';
@@ -54,28 +49,17 @@ public record UserCreateModel(
5449
{ // Automatically generated by IntelliJ
5550
if (o == null || getClass() != o.getClass()) {return false;}
5651
UserCreateModel that = (UserCreateModel) o;
57-
return Objects.equals(theme, that.theme) && Objects.deepEquals(
58-
groups,
59-
that.groups) && Objects.equals(locked, that.locked) && Objects.equals(
52+
return Objects.equals(theme, that.theme) && Objects.equals(
6053
lastName,
61-
that.lastName) && Objects.equals(enabled, that.enabled) && Objects.equals(
62-
firstName,
63-
that.firstName) && Objects.equals(
54+
that.lastName) && Objects.equals(enabled, that.enabled) && Objects.deepEquals(
55+
groups,
56+
that.groups) && Objects.equals(firstName, that.firstName) && Objects.equals(
6457
loginName,
65-
that.loginName) && status == that.status && accountType == that.accountType;
58+
that.loginName) && accountType == that.accountType;
6659
}
6760

6861
@Override public int hashCode()
6962
{ // Automatically generated by IntelliJ
70-
return Objects.hash(
71-
firstName,
72-
lastName,
73-
loginName,
74-
accountType,
75-
enabled,
76-
locked,
77-
status,
78-
theme,
79-
Arrays.hashCode(groups));
63+
return Objects.hash(firstName, lastName, loginName, accountType, enabled, theme, Arrays.hashCode(groups));
8064
}
8165
}

EEDU-Backend/src/test/java/de/gaz/eedu/user/UserServiceTest.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,20 +47,18 @@ public class UserServiceTest extends ServiceTest<Long, UserService, UserEntity,
4747
{
4848
String id = String.valueOf(number);
4949
AccountType type = AccountType.STUDENT;
50-
UserStatus status = UserStatus.PRESENT;
51-
52-
return new UserCreateModel("User", id, "user." + id, type, false, false, status, 1L, new String[0]);
50+
return new UserCreateModel("User", id, "user." + id, type, false, 1L, new String[0]);
5351
}
5452

55-
private @NotNull UserModel model(int number)
53+
private @NotNull UserModel model()
5654
{
57-
String id = String.valueOf(number);
55+
String id = String.valueOf(18);
5856
AccountType type = AccountType.STUDENT;
59-
UserStatus status = UserStatus.PRESENT;
57+
UserStatus status = UserStatus.PROSPECTIVE;
6058
ThemeModel theme = getThemeService().loadByIdSafe(1L);
6159

6260
GroupModel[] groupModel = {new GroupModel("student", new PrivilegeModel[0])};
63-
return new UserModel(number + 1L, "User", id, "user." + id, type, status, groupModel, theme, null);
61+
return new UserModel(18 + 1L, "User", id, "user." + id, type, status, groupModel, theme, null);
6462
}
6563

6664
@Override
@@ -81,7 +79,7 @@ public class UserServiceTest extends ServiceTest<Long, UserService, UserEntity,
8179

8280
@Override
8381
protected @NotNull ServiceTest.Eval<UserCreateModel, UserModel> successEval() {
84-
return Eval.eval(createModel(18), model(18), (request, expect, result) -> {
82+
return Eval.eval(createModel(18), model(), (request, expect, result) -> {
8583
Assertions.assertEquals(expect, result);
8684
Assertions.assertEquals(expect.firstName(), result.firstName());
8785
Assertions.assertEquals(expect.lastName(), result.lastName());
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<div class="container">
2+
3+
<mat-progress-bar *ngIf="!loaded" mode="indeterminate"></mat-progress-bar>
4+
5+
<div *ngIf="loaded && values.length == 0" class="empty-list-content">
6+
<mat-icon class="empty-list-icon">folder_open</mat-icon>
7+
<button (click)="addPressed.emit()" mat-flat-button>Create first entry</button>
8+
</div>
9+
10+
<list
11+
#list
12+
*ngIf="loaded && values.length > 0"
13+
[height]="300"
14+
[itemInfo]="itemInfo()"
15+
[selectionType]="SelectionType.MULTIPLE" [values]="values">
16+
<div style="margin-left: auto">
17+
<button (click)="deletePressed.emit(list.selected)" *ngIf="list.selected.length > 0" mat-icon-button>
18+
<mat-icon>delete</mat-icon>
19+
</button>
20+
<button (click)="addPressed.emit()" mat-icon-button>
21+
<mat-icon>add</mat-icon>
22+
</button>
23+
</div>
24+
</list>
25+
</div>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
.container {
2+
height: 500px;
3+
4+
mat-progress-bar {
5+
width: 50%;
6+
margin: 0;
7+
position: absolute;
8+
top: 50%;
9+
left: 50%;
10+
transform: translate(-50%, -50%);
11+
}
12+
13+
.empty-list-content {
14+
display: flex;
15+
flex-direction: column;
16+
align-items: center;
17+
justify-content: center;
18+
height: 100%;
19+
text-align: center;
20+
21+
.mat-icon {
22+
transform: scale(4);
23+
padding: 30px;
24+
-webkit-text-fill-color: var(--widget-color);
25+
}
26+
27+
button {
28+
margin-top: 8px;
29+
}
30+
}
31+
32+
}
33+
34+
35+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { EntityListComponent } from './entity-list.component';
4+
5+
describe('EntityListComponent', () => {
6+
let component: EntityListComponent;
7+
let fixture: ComponentFixture<EntityListComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
imports: [EntityListComponent]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(EntityListComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {AfterViewInit, Component, EventEmitter, input, InputSignal, Output} from '@angular/core';
2+
import {MatIcon} from "@angular/material/icon";
3+
import {AbstractList, ListItemInfo, SelectionType} from "../../common/abstract-list/abstract-list.component";
4+
import {NgIf} from "@angular/common";
5+
import {MatProgressBar} from "@angular/material/progress-bar";
6+
import {MatButton, MatIconButton} from "@angular/material/button";
7+
import {EntityService} from "../entity-service";
8+
9+
@Component({
10+
selector: 'app-entity-list',
11+
imports: [
12+
MatIcon,
13+
AbstractList,
14+
NgIf,
15+
MatProgressBar,
16+
MatButton,
17+
MatIconButton
18+
],
19+
templateUrl: './entity-list.component.html',
20+
styleUrl: './entity-list.component.scss'
21+
})
22+
export class EntityListComponent<T extends { id: any }> implements AfterViewInit {
23+
24+
protected readonly SelectionType: typeof SelectionType = SelectionType;
25+
@Output() public readonly addPressed: EventEmitter<void> = new EventEmitter<void>;
26+
@Output() public readonly deletePressed: EventEmitter<T[]> = new EventEmitter<T[]>;
27+
28+
public readonly service: InputSignal<EntityService<any, T, any, any> | null> = input<EntityService<any, T, any, any> | null>(null)
29+
public readonly itemInfo: InputSignal<ListItemInfo<T> | null> = input<ListItemInfo<T> | null>(null);
30+
31+
private _values: readonly T[] = [];
32+
33+
public ngAfterViewInit(): void {
34+
if(!this.service())
35+
{
36+
return;
37+
}
38+
this.service()?.value$.subscribe((value: T[]): void => { this._values = value; })
39+
}
40+
41+
protected get loaded(): boolean
42+
{
43+
return this.service()?.fetched || false;
44+
}
45+
46+
protected get values(): readonly T[] {
47+
return this._values;
48+
}
49+
}

0 commit comments

Comments
 (0)