1

Merge pull request #6 from ChUrl/UI-refactor+improvement

Ui refactor+improvement
This commit is contained in:
Christoph
2020-04-12 16:19:42 +02:00
committed by GitHub
69 changed files with 1297 additions and 1295 deletions

View File

@ -1,111 +0,0 @@
package mops.gruppen2.controller;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.User;
import mops.gruppen2.service.CsvService;
import mops.gruppen2.service.GroupService;
import mops.gruppen2.service.IdService;
import mops.gruppen2.service.ProjectionService;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.security.RolesAllowed;
import static mops.gruppen2.service.ControllerService.getGroupType;
import static mops.gruppen2.service.ControllerService.getParent;
import static mops.gruppen2.service.ControllerService.getUserLimit;
import static mops.gruppen2.service.ControllerService.getVisibility;
@SuppressWarnings("SameReturnValue")
@Log4j2
@TraceMethodCalls
@Controller
@RequestMapping("/gruppen2")
public class GroupCreationController {
private final GroupService groupService;
private final ProjectionService projectionService;
public GroupCreationController(GroupService groupService, ProjectionService projectionService) {
this.groupService = groupService;
this.projectionService = projectionService;
}
@RolesAllowed("ROLE_orga")
@GetMapping("/create/orga")
public String getCreateOrga(Model model) {
model.addAttribute("lectures", projectionService.projectLectures());
return "createOrga";
}
@RolesAllowed("ROLE_orga")
@PostMapping("/create/orga")
@CacheEvict(value = "groups", allEntries = true)
public String postCreateOrga(KeycloakAuthenticationToken token,
@RequestParam("title") String title,
@RequestParam("description") String description,
@RequestParam("visibility") boolean isPrivate,
@RequestParam("lecture") boolean isLecture,
@RequestParam("maxInfiniteUsers") boolean isInfinite,
@RequestParam("userMaximum") long userLimit,
@RequestParam("parent") String parent,
@RequestParam(value = "file", required = false) MultipartFile file) {
User user = new User(token);
Group group = groupService.createGroup(user,
title,
description,
getVisibility(isPrivate),
getGroupType(isLecture),
getUserLimit(isInfinite, userLimit),
getParent(parent, isLecture));
groupService.addUsersToGroup(CsvService.readCsvFile(file), group, user);
return "redirect:/gruppen2/details/" + IdService.uuidToString(group.getId());
}
@RolesAllowed("ROLE_studentin")
@GetMapping("/create/student")
public String getCreateStudent(Model model) {
model.addAttribute("lectures", projectionService.projectLectures());
return "createStudent";
}
@RolesAllowed("ROLE_studentin")
@PostMapping("/create/student")
@CacheEvict(value = "groups", allEntries = true)
public String postCreateStudent(KeycloakAuthenticationToken token,
@RequestParam("title") String title,
@RequestParam("description") String description,
@RequestParam("visibility") boolean isPrivate,
@RequestParam("maxInfiniteUsers") boolean isInfinite,
@RequestParam("userMaximum") long userLimit,
@RequestParam("parent") String parent) {
User user = new User(token);
Group group = groupService.createGroup(user,
title,
description,
getVisibility(isPrivate),
GroupType.SIMPLE,
getUserLimit(isInfinite, userLimit),
getParent(parent, false));
return "redirect:/gruppen2/details/" + IdService.uuidToString(group.getId());
}
}

View File

@ -28,7 +28,6 @@ public class Group {
//TODO: Single Type for Public/Private/Lecture? //TODO: Single Type for Public/Private/Lecture?
private GroupType type; private GroupType type;
private Visibility visibility;
private String title; private String title;
private String description; private String description;

View File

@ -1,6 +1,7 @@
package mops.gruppen2.domain; package mops.gruppen2.domain;
public enum GroupType { public enum GroupType {
SIMPLE, PUBLIC,
PRIVATE,
LECTURE LECTURE
} }

View File

@ -0,0 +1,6 @@
package mops.gruppen2.domain;
public enum Limit {
INFINITE,
LOCKED
}

View File

@ -1,6 +0,0 @@
package mops.gruppen2.domain;
public enum Visibility {
PUBLIC,
PRIVATE
}

View File

@ -6,7 +6,6 @@ import lombok.ToString;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType; import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.Visibility;
import java.util.UUID; import java.util.UUID;
@ -16,15 +15,13 @@ import java.util.UUID;
@Log4j2 @Log4j2
public class CreateGroupEvent extends Event { public class CreateGroupEvent extends Event {
private Visibility groupVisibility;
private UUID groupParent; private UUID groupParent;
private GroupType groupType; private GroupType groupType;
public CreateGroupEvent(UUID groupId, String userId, UUID parent, GroupType type, Visibility visibility) { public CreateGroupEvent(UUID groupId, String userId, UUID parent, GroupType type) {
super(groupId, userId); super(groupId, userId);
groupParent = parent; groupParent = parent;
groupType = type; groupType = type;
groupVisibility = visibility;
} }
@Override @Override
@ -32,7 +29,6 @@ public class CreateGroupEvent extends Event {
group.setId(groupId); group.setId(groupId);
group.setParent(groupParent); group.setParent(groupParent);
group.setType(groupType); group.setType(groupType);
group.setVisibility(groupVisibility);
log.trace("\t\t\t\t\tNeue Gruppe: {}", group.toString()); log.trace("\t\t\t\t\tNeue Gruppe: {}", group.toString());
} }

View File

@ -29,7 +29,6 @@ public class DeleteGroupEvent extends Event {
group.getMembers().clear(); group.getMembers().clear();
group.setTitle(null); group.setTitle(null);
group.setDescription(null); group.setDescription(null);
group.setVisibility(null);
group.setType(null); group.setType(null);
group.setParent(null); group.setParent(null);
group.setUserLimit(0L); group.setUserLimit(0L);

View File

@ -5,6 +5,6 @@ import org.springframework.http.HttpStatus;
public class GroupNotFoundException extends EventException { public class GroupNotFoundException extends EventException {
public GroupNotFoundException(String info) { public GroupNotFoundException(String info) {
super(HttpStatus.NOT_FOUND, "Gruppe wurde nicht gefunden.", info); super(HttpStatus.NOT_FOUND, "Die Gruppe existiert nicht oder wurde gelöscht.", info);
} }
} }

View File

@ -1,8 +1,8 @@
package mops.gruppen2.service; package mops.gruppen2.domain.service;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.api.GroupRequestWrapper; import mops.gruppen2.web.api.GroupRequestWrapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;

View File

@ -1,4 +1,4 @@
package mops.gruppen2.service; package mops.gruppen2.domain.service;
import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.dataformat.csv.CsvMapper; import com.fasterxml.jackson.dataformat.csv.CsvMapper;

View File

@ -1,14 +1,14 @@
package mops.gruppen2.service; package mops.gruppen2.domain.service;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.User; import mops.gruppen2.domain.User;
import mops.gruppen2.domain.dto.EventDTO;
import mops.gruppen2.domain.event.AddUserEvent; import mops.gruppen2.domain.event.AddUserEvent;
import mops.gruppen2.domain.event.CreateGroupEvent; import mops.gruppen2.domain.event.CreateGroupEvent;
import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.exception.BadPayloadException; import mops.gruppen2.domain.exception.BadPayloadException;
import mops.gruppen2.repository.EventRepository; import mops.gruppen2.persistance.EventRepository;
import mops.gruppen2.persistance.dto.EventDTO;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList; import java.util.ArrayList;

View File

@ -1,11 +1,10 @@
package mops.gruppen2.service; package mops.gruppen2.domain.service;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType; import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.Role; import mops.gruppen2.domain.Role;
import mops.gruppen2.domain.User; import mops.gruppen2.domain.User;
import mops.gruppen2.domain.Visibility;
import mops.gruppen2.domain.event.AddUserEvent; import mops.gruppen2.domain.event.AddUserEvent;
import mops.gruppen2.domain.event.CreateGroupEvent; import mops.gruppen2.domain.event.CreateGroupEvent;
import mops.gruppen2.domain.event.DeleteGroupEvent; import mops.gruppen2.domain.event.DeleteGroupEvent;
@ -51,7 +50,6 @@ public class GroupService {
public Group createGroup(User user, public Group createGroup(User user,
String title, String title,
String description, String description,
Visibility visibility,
GroupType groupType, GroupType groupType,
long userLimit, long userLimit,
UUID parent) { UUID parent) {
@ -59,8 +57,7 @@ public class GroupService {
// Regeln: // Regeln:
// isPrivate -> !isLecture // isPrivate -> !isLecture
// isLecture -> !isPrivate // isLecture -> !isPrivate
ValidationService.validateFlags(visibility, groupType); Group group = createGroup(user, parent, groupType);
Group group = createGroup(user, parent, groupType, visibility);
// Die Reihenfolge ist wichtig, da der ausführende User Admin sein muss // Die Reihenfolge ist wichtig, da der ausführende User Admin sein muss
addUser(user, group); addUser(user, group);
@ -133,12 +130,11 @@ public class GroupService {
/** /**
* Erzeugt eine Gruppe, speichert diese und gibt diese zurück. * Erzeugt eine Gruppe, speichert diese und gibt diese zurück.
*/ */
private Group createGroup(User user, UUID parent, GroupType groupType, Visibility visibility) { private Group createGroup(User user, UUID parent, GroupType groupType) {
Event event = new CreateGroupEvent(UUID.randomUUID(), Event event = new CreateGroupEvent(UUID.randomUUID(),
user.getId(), user.getId(),
parent, parent,
groupType, groupType);
visibility);
Group group = new Group(); Group group = new Group();
event.apply(group); event.apply(group);
@ -211,7 +207,6 @@ public class GroupService {
*/ */
public void updateTitle(User user, Group group, String title) { public void updateTitle(User user, Group group, String title) {
ValidationService.throwIfNoAdmin(group, user); ValidationService.throwIfNoAdmin(group, user);
ValidationService.validateTitle(title.trim());
if (title.trim().equals(group.getTitle())) { if (title.trim().equals(group.getTitle())) {
return; return;
@ -230,7 +225,6 @@ public class GroupService {
*/ */
public void updateDescription(User user, Group group, String description) { public void updateDescription(User user, Group group, String description) {
ValidationService.throwIfNoAdmin(group, user); ValidationService.throwIfNoAdmin(group, user);
ValidationService.validateDescription(description.trim());
if (description.trim().equals(group.getDescription())) { if (description.trim().equals(group.getDescription())) {
return; return;
@ -267,13 +261,18 @@ public class GroupService {
*/ */
public void updateUserLimit(User user, Group group, long userLimit) { public void updateUserLimit(User user, Group group, long userLimit) {
ValidationService.throwIfNoAdmin(group, user); ValidationService.throwIfNoAdmin(group, user);
ValidationService.validateUserLimit(userLimit, group);
if (userLimit == group.getUserLimit()) { if (userLimit == group.getUserLimit()) {
return; return;
} }
Event event = new UpdateUserLimitEvent(group, user, userLimit); Event event;
if (userLimit < group.getMembers().size()) {
event = new UpdateUserLimitEvent(group, user, group.getMembers().size());
} else {
event = new UpdateUserLimitEvent(group, user, userLimit);
}
event.apply(group); event.apply(group);
eventStoreService.saveEvent(event); eventStoreService.saveEvent(event);

View File

@ -1,4 +1,4 @@
package mops.gruppen2.service; package mops.gruppen2.domain.service;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;

View File

@ -1,11 +1,11 @@
package mops.gruppen2.service; package mops.gruppen2.domain.service;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.dto.InviteLinkDTO;
import mops.gruppen2.domain.exception.InvalidInviteException; import mops.gruppen2.domain.exception.InvalidInviteException;
import mops.gruppen2.domain.exception.NoInviteExistException; import mops.gruppen2.domain.exception.NoInviteExistException;
import mops.gruppen2.repository.InviteRepository; import mops.gruppen2.persistance.InviteRepository;
import mops.gruppen2.persistance.dto.InviteLinkDTO;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.UUID; import java.util.UUID;

View File

@ -1,4 +1,4 @@
package mops.gruppen2.service; package mops.gruppen2.domain.service;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;

View File

@ -1,10 +1,9 @@
package mops.gruppen2.service; package mops.gruppen2.domain.service;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType; import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.User; import mops.gruppen2.domain.User;
import mops.gruppen2.domain.Visibility;
import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.exception.EventException; import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.exception.GroupNotFoundException; import mops.gruppen2.domain.exception.GroupNotFoundException;
@ -131,7 +130,7 @@ public class ProjectionService {
List<Group> groups = projectGroups(events); List<Group> groups = projectGroups(events);
return groups.stream() return groups.stream()
.filter(group -> group.getVisibility() == Visibility.PUBLIC) .filter(group -> group.getType() == GroupType.PUBLIC)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }

View File

@ -1,4 +1,4 @@
package mops.gruppen2.service; package mops.gruppen2.domain.service;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;

View File

@ -1,16 +1,17 @@
package mops.gruppen2.service; package mops.gruppen2.domain.service;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType; import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.User; import mops.gruppen2.domain.User;
import mops.gruppen2.domain.Visibility;
import mops.gruppen2.domain.exception.BadParameterException; import mops.gruppen2.domain.exception.BadParameterException;
import mops.gruppen2.domain.exception.GroupFullException; import mops.gruppen2.domain.exception.GroupFullException;
import mops.gruppen2.domain.exception.NoAccessException; import mops.gruppen2.domain.exception.NoAccessException;
import mops.gruppen2.domain.exception.NoAdminAfterActionException; import mops.gruppen2.domain.exception.NoAdminAfterActionException;
import mops.gruppen2.domain.exception.UserAlreadyExistsException; import mops.gruppen2.domain.exception.UserAlreadyExistsException;
import mops.gruppen2.domain.exception.UserNotFoundException; import mops.gruppen2.domain.exception.UserNotFoundException;
import mops.gruppen2.web.form.CreateForm;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import static mops.gruppen2.domain.Role.ADMIN; import static mops.gruppen2.domain.Role.ADMIN;
@ -110,36 +111,10 @@ public final class ValidationService {
// ##################################### VALIDATE FIELDS ##################################### // ##################################### VALIDATE FIELDS #####################################
public static void validateCreateForm(KeycloakAuthenticationToken token, CreateForm form) {
//TODO: max title length? if (!token.getAccount().getRoles().contains("orga")
public static void validateTitle(String title) { && form.getType() == GroupType.LECTURE) {
if (title == null || title.trim().isEmpty()) { throw new BadParameterException("Eine Veranstaltung kann nur von ORGA erstellt werden.");
log.error("Der Titel {} ist fehlerhaft!", title);
throw new BadParameterException("Der Titel darf nicht leer sein!");
}
}
//TODO: max description length?
public static void validateDescription(String description) {
if (description == null || description.trim().isEmpty()) {
log.error("Die Beschreibung {} ist fehlerhaft!", description);
throw new BadParameterException("Die Beschreibung darf nicht leer sein!");
}
}
public static void validateFlags(Visibility visibility, GroupType groupType) {
if (visibility == Visibility.PRIVATE && groupType == GroupType.LECTURE) {
throw new BadParameterException("Eine Veranstaltung kann nicht privat sein!");
}
}
public static void validateUserLimit(long userLimit, Group group) {
if (userLimit < 1) {
throw new BadParameterException("Das Userlimit muss größer als 1 sein!");
}
if (userLimit < group.getMembers().size()) {
throw new BadParameterException("Das Userlimit kann nicht unter der momentanen Mitgliederanzahl sein!");
} }
} }
} }

View File

@ -1,6 +1,6 @@
package mops.gruppen2.repository; package mops.gruppen2.persistance;
import mops.gruppen2.domain.dto.EventDTO; import mops.gruppen2.persistance.dto.EventDTO;
import org.springframework.data.jdbc.repository.query.Query; import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;

View File

@ -1,6 +1,6 @@
package mops.gruppen2.repository; package mops.gruppen2.persistance;
import mops.gruppen2.domain.dto.InviteLinkDTO; import mops.gruppen2.persistance.dto.InviteLinkDTO;
import org.springframework.data.jdbc.repository.query.Modifying; import org.springframework.data.jdbc.repository.query.Modifying;
import org.springframework.data.jdbc.repository.query.Query; import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;

View File

@ -1,4 +1,4 @@
package mops.gruppen2.domain.dto; package mops.gruppen2.persistance.dto;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;

View File

@ -1,4 +1,4 @@
package mops.gruppen2.domain.dto; package mops.gruppen2.persistance.dto;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;

View File

@ -1,44 +0,0 @@
package mops.gruppen2.service;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.Visibility;
import org.springframework.stereotype.Service;
import java.util.UUID;
@Service
@Log4j2
public final class ControllerService {
private ControllerService() {}
public static Visibility getVisibility(boolean isPrivate) {
return isPrivate ? Visibility.PRIVATE : Visibility.PUBLIC;
}
public static GroupType getGroupType(boolean isLecture) {
return isLecture ? GroupType.LECTURE : GroupType.SIMPLE;
}
/**
* Wenn die maximale Useranzahl unendlich ist, wird das Maximum auf 100000 gesetzt.
* Praktisch gibt es also maximal 100000 Nutzer pro Gruppe.
*
* @param isInfinite Gibt an, ob es unendlich viele User geben soll
* @param userLimit Das Maximum an Usern, falls es eins gibt
*
* @return Maximum an Usern
*/
public static long getUserLimit(boolean isInfinite, long userLimit) {
return isInfinite ? Long.MAX_VALUE : userLimit;
}
/**
* Ermittelt die UUID des Parents, falls vorhanden.
*/
public static UUID getParent(String parent, boolean isLecture) {
return isLecture ? IdService.emptyUUID() : IdService.stringToUUID(parent);
}
}

View File

@ -1,4 +1,4 @@
package mops.gruppen2.controller; package mops.gruppen2.web;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@ -7,11 +7,11 @@ import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls; import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.User; import mops.gruppen2.domain.User;
import mops.gruppen2.domain.api.GroupRequestWrapper; import mops.gruppen2.domain.service.APIService;
import mops.gruppen2.service.APIService; import mops.gruppen2.domain.service.EventStoreService;
import mops.gruppen2.service.EventStoreService; import mops.gruppen2.domain.service.IdService;
import mops.gruppen2.service.IdService; import mops.gruppen2.domain.service.ProjectionService;
import mops.gruppen2.service.ProjectionService; import mops.gruppen2.web.api.GroupRequestWrapper;
import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;

View File

@ -0,0 +1,76 @@
package mops.gruppen2.web;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.service.CsvService;
import mops.gruppen2.domain.service.GroupService;
import mops.gruppen2.domain.service.IdService;
import mops.gruppen2.domain.service.ProjectionService;
import mops.gruppen2.domain.service.ValidationService;
import mops.gruppen2.web.form.CreateForm;
import mops.gruppen2.web.form.MetaForm;
import mops.gruppen2.web.form.UserLimitForm;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.security.RolesAllowed;
import javax.validation.Valid;
@SuppressWarnings("SameReturnValue")
@Log4j2
@TraceMethodCalls
@Controller
@RequestMapping("/gruppen2")
public class GroupCreationController {
private final GroupService groupService;
private final ProjectionService projectionService;
public GroupCreationController(GroupService groupService, ProjectionService projectionService) {
this.groupService = groupService;
this.projectionService = projectionService;
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/create")
public String getCreate(Model model) {
model.addAttribute("lectures", projectionService.projectLectures());
return "create";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/create")
@CacheEvict(value = "groups", allEntries = true)
public String postCreateOrga(KeycloakAuthenticationToken token,
@Valid CreateForm create,
@Valid MetaForm meta,
@Valid UserLimitForm limit) {
// Zusätzlicher check: studentin kann keine lecture erstellen
ValidationService.validateCreateForm(token, create);
User user = new User(token);
Group group = groupService.createGroup(user,
meta.getTitle(),
meta.getDescription(),
create.getType(),
limit.getUserlimit(),
create.getParent());
// ROLE_studentin kann kein CSV importieren
if (token.getAccount().getRoles().contains("orga")) {
groupService.addUsersToGroup(CsvService.readCsvFile(create.getFile()), group, user);
}
return "redirect:/gruppen2/details/" + IdService.uuidToString(group.getId());
}
}

View File

@ -1,15 +1,17 @@
package mops.gruppen2.controller; package mops.gruppen2.web;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls; import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.User; import mops.gruppen2.domain.User;
import mops.gruppen2.service.CsvService; import mops.gruppen2.domain.service.CsvService;
import mops.gruppen2.service.GroupService; import mops.gruppen2.domain.service.GroupService;
import mops.gruppen2.service.IdService; import mops.gruppen2.domain.service.IdService;
import mops.gruppen2.service.InviteService; import mops.gruppen2.domain.service.InviteService;
import mops.gruppen2.service.ProjectionService; import mops.gruppen2.domain.service.ProjectionService;
import mops.gruppen2.service.ValidationService; import mops.gruppen2.domain.service.ValidationService;
import mops.gruppen2.web.form.MetaForm;
import mops.gruppen2.web.form.UserLimitForm;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -23,6 +25,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.annotation.security.RolesAllowed; import javax.annotation.security.RolesAllowed;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.UUID; import java.util.UUID;
@SuppressWarnings("SameReturnValue") @SuppressWarnings("SameReturnValue")
@ -40,13 +43,13 @@ public class GroupDetailsController {
this.inviteService = inviteService; this.inviteService = inviteService;
this.groupService = groupService; this.groupService = groupService;
this.projectionService = projectionService; this.projectionService = projectionService;
} }
@RolesAllowed({"ROLE_orga", "ROLE_studentin"}) @RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/details/{id}") @GetMapping("/details/{id}")
public String getDetailsPage(KeycloakAuthenticationToken token, public String getDetailsPage(KeycloakAuthenticationToken token,
Model model, Model model,
HttpServletRequest request,
@PathVariable("id") String groupId) { @PathVariable("id") String groupId) {
User user = new User(token); User user = new User(token);
@ -56,21 +59,15 @@ public class GroupDetailsController {
UUID parentId = group.getParent(); UUID parentId = group.getParent();
Group parent = projectionService.projectParent(parentId); Group parent = projectionService.projectParent(parentId);
// Invite Link
String actualURL = request.getRequestURL().toString();
String serverURL = actualURL.substring(0, actualURL.indexOf("gruppen2/"));
String link = serverURL + "gruppen2/join/" + inviteService.getLinkByGroup(group);
model.addAttribute("group", group); model.addAttribute("group", group);
model.addAttribute("parent", parent); model.addAttribute("parent", parent);
model.addAttribute("link", link);
// Detailseite für nicht-Mitglieder // Detailseite für nicht-Mitglieder
if (!ValidationService.checkIfMember(group, user)) { if (!ValidationService.checkIfMember(group, user)) {
return "detailsNoMember"; return "preview";
} }
return "detailsMember"; return "details";
} }
@RolesAllowed({"ROLE_orga", "ROLE_studentin"}) @RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@ -82,6 +79,10 @@ public class GroupDetailsController {
User user = new User(token); User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
if (ValidationService.checkIfMember(group, user)) {
return "redirect:/gruppen2/details/" + groupId;
}
groupService.addUser(user, group); groupService.addUser(user, group);
return "redirect:/gruppen2/details/" + groupId; return "redirect:/gruppen2/details/" + groupId;
@ -96,112 +97,87 @@ public class GroupDetailsController {
User user = new User(token); User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
ValidationService.throwIfNoMember(group, user);
groupService.deleteUser(user, group); groupService.deleteUser(user, group);
return "redirect:/gruppen2"; return "redirect:/gruppen2";
} }
@RolesAllowed({"ROLE_orga", "ROLE_studentin"}) @RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/details/{id}/destroy") @GetMapping("/details/{id}/edit")
@CacheEvict(value = "groups", allEntries = true) public String getDetailsEdit(KeycloakAuthenticationToken token,
public String postDetailsDestroy(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.deleteGroup(user, group);
return "redirect:/gruppen2";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/details/{id}/meta")
public String getDetailsMeta(KeycloakAuthenticationToken token,
Model model, Model model,
HttpServletRequest request,
@PathVariable("id") String groupId) { @PathVariable("id") String groupId) {
User user = new User(token); User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
ValidationService.throwIfNoAdmin(group, user); // Invite Link
String actualURL = request.getRequestURL().toString();
model.addAttribute("group", group); String serverURL = actualURL.substring(0, actualURL.indexOf("gruppen2/"));
String link = serverURL + "gruppen2/join/" + inviteService.getLinkByGroup(group);
return "changeMetadata";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/details/{id}/meta/update")
@CacheEvict(value = "groups", allEntries = true)
public String postDetailsMetaUpdate(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId,
@RequestParam("title") String title,
@RequestParam("description") String description) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.updateTitle(user, group, title);
groupService.updateDescription(user, group, description);
return "redirect:/gruppen2/details/" + groupId;
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/details/{id}/members")
public String getDetailsMembers(KeycloakAuthenticationToken token,
Model model,
@PathVariable("id") String groupId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
ValidationService.throwIfNoAdmin(group, user); ValidationService.throwIfNoAdmin(group, user);
model.addAttribute("group", group); model.addAttribute("group", group);
model.addAttribute("link", link);
return "editMembers"; return "edit";
} }
@RolesAllowed({"ROLE_orga", "ROLE_studentin"}) @RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/details/{id}/members/update/userlimit") @PostMapping("/details/{id}/edit/meta")
@CacheEvict(value = "groups", allEntries = true) @CacheEvict(value = "groups", allEntries = true)
public String postDetailsMembersUpdateUserLimit(KeycloakAuthenticationToken token, public String postDetailsEditMeta(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId, @PathVariable("id") String groupId,
@RequestParam("maximum") long userLimit) { @Valid MetaForm form) {
User user = new User(token); User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.updateUserLimit(user, group, userLimit); groupService.updateTitle(user, group, form.getTitle());
groupService.updateDescription(user, group, form.getDescription());
return "redirect:/gruppen2/details/" + groupId + "/members"; return "redirect:/gruppen2/details/" + groupId + "/edit";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/details/{id}/edit/userlimit")
@CacheEvict(value = "groups", allEntries = true)
public String postDetailsEditUserLimit(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId,
@Valid UserLimitForm form) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.updateUserLimit(user, group, form.getUserlimit());
return "redirect:/gruppen2/details/" + groupId + "/edit";
} }
@RolesAllowed("ROLE_orga") @RolesAllowed("ROLE_orga")
@PostMapping("/details/{id}/members/update/csv") @PostMapping("/details/{id}/edit/csv")
@CacheEvict(value = "groups", allEntries = true) @CacheEvict(value = "groups", allEntries = true)
public String postDetailsMembersUpdateCsv(KeycloakAuthenticationToken token, public String postDetailsEditCsv(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId, @PathVariable("id") String groupId,
@RequestParam(value = "file", required = false) MultipartFile file) { @RequestParam(value = "file", required = false) MultipartFile file) {
User user = new User(token); User user = new User(token);
Group group = projectionService.projectSingleGroup(IdService.stringToUUID(groupId)); Group group = projectionService.projectSingleGroup(IdService.stringToUUID(groupId));
groupService.addUsersToGroup(CsvService.readCsvFile(file), group, user); groupService.addUsersToGroup(CsvService.readCsvFile(file), group, user);
return "redirect:/gruppen2/details/" + groupId + "/members"; return "redirect:/gruppen2/details/" + groupId + "/edit";
} }
//TODO: Method + view for /details/{id}/members/{id}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"}) @RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/details/{id}/members/{userid}/update/role") @PostMapping("/details/{id}/edit/role/{userid}")
@CacheEvict(value = "groups", allEntries = true) @CacheEvict(value = "groups", allEntries = true)
public String postDetailsMembersUpdateRole(KeycloakAuthenticationToken token, public String postDetailsEditRole(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId, @PathVariable("id") String groupId,
@PathVariable("userid") String userId) { @PathVariable("userid") String userId) {
User user = new User(token); User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
@ -215,15 +191,15 @@ public class GroupDetailsController {
return "redirect:/gruppen2/details/" + groupId; return "redirect:/gruppen2/details/" + groupId;
} }
return "redirect:/gruppen2/details/" + groupId + "/members"; return "redirect:/gruppen2/details/" + groupId + "/edit";
} }
@RolesAllowed({"ROLE_orga", "ROLE_studentin"}) @RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/details/{id}/members/{userid}/delete") @PostMapping("/details/{id}/edit/delete/{userid}")
@CacheEvict(value = "groups", allEntries = true) @CacheEvict(value = "groups", allEntries = true)
public String postDetailsMembersDelete(KeycloakAuthenticationToken token, public String postDetailsEditDelete(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId, @PathVariable("id") String groupId,
@PathVariable("userid") String userId) { @PathVariable("userid") String userId) {
User user = new User(token); User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
@ -235,6 +211,22 @@ public class GroupDetailsController {
groupService.deleteUser(new User(userId), group); groupService.deleteUser(new User(userId), group);
} }
return "redirect:/gruppen2/details/" + groupId + "/members"; return "redirect:/gruppen2/details/" + groupId + "/edit";
} }
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/details/{id}/edit/destroy")
@CacheEvict(value = "groups", allEntries = true)
public String postDetailsEditDestroy(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.deleteGroup(user, group);
return "redirect:/gruppen2";
}
//TODO: Method + view for /details/{id}/member/{id}
} }

View File

@ -1,10 +1,10 @@
package mops.gruppen2.controller; package mops.gruppen2.web;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCall; import mops.gruppen2.aspect.annotation.TraceMethodCall;
import mops.gruppen2.domain.User; import mops.gruppen2.domain.User;
import mops.gruppen2.domain.exception.PageNotFoundException; import mops.gruppen2.domain.exception.PageNotFoundException;
import mops.gruppen2.service.ProjectionService; import mops.gruppen2.domain.service.ProjectionService;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
@ -26,10 +26,10 @@ public class GruppenfindungController {
} }
// For convenience // For convenience
@GetMapping("") //@GetMapping("")
public String redirect() { //public String redirect() {
return "redirect:/gruppen2"; // return "redirect:/gruppen2";
} //}
@TraceMethodCall @TraceMethodCall
@RolesAllowed({"ROLE_orga", "ROLE_studentin"}) @RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@ -39,7 +39,7 @@ public class GruppenfindungController {
User user = new User(token); User user = new User(token);
model.addAttribute("gruppen", projectionService.projectUserGroups(user)); model.addAttribute("groups", projectionService.projectUserGroups(user));
return "index"; return "index";
} }

View File

@ -1,10 +1,9 @@
package mops.gruppen2.controller; package mops.gruppen2.web;
import mops.gruppen2.domain.Account; import mops.gruppen2.domain.Account;
import mops.gruppen2.domain.GroupType; import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.Role; import mops.gruppen2.domain.Role;
import mops.gruppen2.domain.User; import mops.gruppen2.domain.User;
import mops.gruppen2.domain.Visibility;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ControllerAdvice;
@ -24,12 +23,12 @@ public class ModelAttributeControllerAdvice {
model.addAttribute("user", new User(token)); model.addAttribute("user", new User(token));
} }
// Add enums
model.addAttribute("member", Role.MEMBER); model.addAttribute("member", Role.MEMBER);
model.addAttribute("admin", Role.ADMIN); model.addAttribute("admin", Role.ADMIN);
model.addAttribute("public", Visibility.PUBLIC); model.addAttribute("public", GroupType.PUBLIC);
model.addAttribute("private", Visibility.PRIVATE); model.addAttribute("private", GroupType.PRIVATE);
model.addAttribute("lecture", GroupType.LECTURE); model.addAttribute("lecture", GroupType.LECTURE);
model.addAttribute("simple", GroupType.SIMPLE);
} }
} }

View File

@ -1,14 +1,14 @@
package mops.gruppen2.controller; package mops.gruppen2.web;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls; import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.User; import mops.gruppen2.domain.User;
import mops.gruppen2.domain.Visibility; import mops.gruppen2.domain.service.InviteService;
import mops.gruppen2.service.InviteService; import mops.gruppen2.domain.service.ProjectionService;
import mops.gruppen2.service.ProjectionService; import mops.gruppen2.domain.service.SearchService;
import mops.gruppen2.service.SearchService; import mops.gruppen2.domain.service.ValidationService;
import mops.gruppen2.service.ValidationService;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
@ -57,7 +57,7 @@ public class SearchAndInviteController {
User user = new User(token); User user = new User(token);
List<Group> groups = searchService.searchPublicGroups(search, user); List<Group> groups = searchService.searchPublicGroups(search, user);
model.addAttribute("gruppen", groups); model.addAttribute("groups", groups);
return "search"; return "search";
} }
@ -74,7 +74,7 @@ public class SearchAndInviteController {
model.addAttribute("group", group); model.addAttribute("group", group);
// Gruppe öffentlich // Gruppe öffentlich
if (group.getVisibility() == Visibility.PUBLIC) { if (group.getType() == GroupType.PUBLIC) {
return "redirect:/gruppen2/details/" + group.getId(); return "redirect:/gruppen2/details/" + group.getId();
} }
@ -83,6 +83,6 @@ public class SearchAndInviteController {
return "redirect:/gruppen2/details/" + group.getId(); return "redirect:/gruppen2/details/" + group.getId();
} }
return "joinprivate"; return "link";
} }
} }

View File

@ -1,4 +1,4 @@
package mops.gruppen2.domain.api; package mops.gruppen2.web.api;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;

View File

@ -0,0 +1,29 @@
package mops.gruppen2.web.form;
import lombok.Data;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.service.IdService;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotBlank;
import java.util.UUID;
@Data
public class CreateForm {
@NotBlank
String type;
@NotBlank
String parent;
MultipartFile file;
public GroupType getType() {
return GroupType.valueOf(type);
}
public UUID getParent() {
return getType() == GroupType.LECTURE ? IdService.emptyUUID() : IdService.stringToUUID(parent);
}
}

View File

@ -0,0 +1,18 @@
package mops.gruppen2.web.form;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
@Data
public class MetaForm {
@NotBlank
@Size(min = 4, max = 128)
String title;
@NotBlank
@Size(min = 4, max = 512)
String description;
}

View File

@ -0,0 +1,14 @@
package mops.gruppen2.web.form;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
@Data
public class UserLimitForm {
@Min(1)
@Max(999_999)
long userlimit;
}

View File

@ -0,0 +1,158 @@
INSERT INTO event
VALUES (1, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '07FEA694', 'CreateGroupEvent',
'{"type":"CreateGroupEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"07FEA694","groupParent":null,"groupType":"PUBLIC"}'),
(2, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '2', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"2","givenname":"Marcos","familyname":"Glover","email":"Marcos.Glover@mail.de"}'),
(3, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '2', 'UpdateUserLimitEvent',
'{"type":"UpdateUserLimitEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"2","userLimit":"1000"}'),
(4, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '5CFBF1AF', 'UpdateGroupTitleEvent',
'{"type":"UpdateGroupTitleEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"5CFBF1AF","newGroupTitle":"Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra"}'),
(5, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '41E11CBC', 'UpdateGroupDescriptionEvent',
'{"type":"UpdateGroupDescriptionEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"41E11CBC","newGroupDescription":"Have you seen my Bear Tibbers? Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyrav"}'),
(6, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '3', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"3","givenname":"Antwan","familyname":"Smitham","email":"Antwan.Smitham@mail.de"}'),
(7, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '4', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"4","givenname":"Numbers","familyname":"Rice","email":"Numbers.Rice@mail.de"}'),
(8, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '5', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"5","givenname":"Charlette","familyname":"Brakus","email":"Charlette.Brakus@mail.de"}'),
(9, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '6', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"6","givenname":"Alethea","familyname":"Kessler","email":"Alethea.Kessler@mail.de"}'),
(10, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '7', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"7","givenname":"Daren","familyname":"Kreiger","email":"Daren.Kreiger@mail.de"}'),
(11, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '8', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"8","givenname":"Gabriel","familyname":"Feeney","email":"Gabriel.Feeney@mail.de"}'),
(12, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '9', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"9","givenname":"Roosevelt","familyname":"Buckridge","email":"Roosevelt.Buckridge@mail.de"}'),
(13, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '10', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"10","givenname":"Eduardo","familyname":"Lowe","email":"Eduardo.Lowe@mail.de"}'),
(14, '555a531c-8b80-491c-88b4-8fc18170d318', '2D7D9CE0', 'CreateGroupEvent',
'{"type":"CreateGroupEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"2D7D9CE0","groupParent":null,"groupType":"PRIVATE"}'),
(15, '555a531c-8b80-491c-88b4-8fc18170d318', 'orga', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"orga","givenname":"Jerald","familyname":"Ward","email":"Jerald.Ward@mail.de"}'),
(16, '555a531c-8b80-491c-88b4-8fc18170d318', 'orga', 'UpdateRoleEvent',
'{"type":"UpdateRoleEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"orga","newRole":"ADMIN"}'),
(17, '555a531c-8b80-491c-88b4-8fc18170d318', '2', 'UpdateUserLimitEvent',
'{"type":"UpdateUserLimitEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"2","userLimit":"1000"}'),
(18, '555a531c-8b80-491c-88b4-8fc18170d318', '5CFBF1AF', 'UpdateGroupTitleEvent',
'{"type":"UpdateGroupTitleEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"5CFBF1AF","newGroupTitle":"Zyra"}'),
(19, '555a531c-8b80-491c-88b4-8fc18170d318', 'EF050D58', 'UpdateGroupDescriptionEvent',
'{"type":"UpdateGroupDescriptionEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"EF050D58","newGroupDescription":"Not Draven Draaaaven."}'),
(20, '555a531c-8b80-491c-88b4-8fc18170d318', '3', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"3","givenname":"Jesica","familyname":"OKeefe","email":"Jesica.OKeefe@mail.de"}'),
(21, '555a531c-8b80-491c-88b4-8fc18170d318', '4', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"4","givenname":"Jess","familyname":"Schamberger","email":"Jess.Schamberger@mail.de"}'),
(22, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', 'E7E2EC5F', 'CreateGroupEvent',
'{"type":"CreateGroupEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"E7E2EC5F","groupParent":null,"groupType":"LECTURE"}'),
(23, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', 'orga', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"orga","givenname":"Hana","familyname":"Connelly","email":"Hana.Connelly@mail.de"}'),
(24, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '2', 'UpdateUserLimitEvent',
'{"type":"UpdateUserLimitEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"2","userLimit":"1000"}'),
(25, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '4EDE191D', 'UpdateGroupTitleEvent',
'{"type":"UpdateGroupTitleEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"4EDE191D","newGroupTitle":"Vi"}'),
(26, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '6EF0B5F9', 'UpdateGroupDescriptionEvent',
'{"type":"UpdateGroupDescriptionEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"6EF0B5F9","newGroupDescription":"Ready to set the world on fire..."}'),
(27, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '3', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"3","givenname":"Steve","familyname":"Swaniawski","email":"Steve.Swaniawski@mail.de"}'),
(28, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '4', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"4","givenname":"Natalie","familyname":"Anderson","email":"Natalie.Anderson@mail.de"}'),
(29, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '5', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"5","givenname":"Asuncion","familyname":"Koss","email":"Asuncion.Koss@mail.de"}'),
(30, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '6', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"6","givenname":"Dana","familyname":"Kemmer","email":"Dana.Kemmer@mail.de"}'),
(31, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '7', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"7","givenname":"Herbert","familyname":"Moen","email":"Herbert.Moen@mail.de"}'),
(32, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '8', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"8","givenname":"Dick","familyname":"Goodwin","email":"Dick.Goodwin@mail.de"}'),
(33, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '9', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"9","givenname":"Deangelo","familyname":"Hoeger","email":"Deangelo.Hoeger@mail.de"}'),
(34, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '10', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"10","givenname":"Zina","familyname":"Abernathy","email":"Zina.Abernathy@mail.de"}'),
(35, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '11', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"11","givenname":"Karima","familyname":"Adams","email":"Karima.Adams@mail.de"}'),
(36, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '12', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"12","givenname":"Evette","familyname":"Price","email":"Evette.Price@mail.de"}'),
(37, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '13', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"13","givenname":"Chester","familyname":"Mayert","email":"Chester.Mayert@mail.de"}'),
(38, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '14', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"14","givenname":"Trang","familyname":"Watsica","email":"Trang.Watsica@mail.de"}'),
(39, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '15', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"15","givenname":"Cameron","familyname":"Terry","email":"Cameron.Terry@mail.de"}'),
(40, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '16', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"16","givenname":"Tynisha","familyname":"Grant","email":"Tynisha.Grant@mail.de"}'),
(41, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '17', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"17","givenname":"Leila","familyname":"Thiel","email":"Leila.Thiel@mail.de"}'),
(42, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '18', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"18","givenname":"Lorenzo","familyname":"Weimann","email":"Lorenzo.Weimann@mail.de"}'),
(43, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '19', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"19","givenname":"Alonzo","familyname":"Keebler","email":"Alonzo.Keebler@mail.de"}'),
(44, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '20', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"20","givenname":"Maximo","familyname":"Olson","email":"Maximo.Olson@mail.de"}'),
(45, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '21', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"21","givenname":"Lisa","familyname":"Konopelski","email":"Lisa.Konopelski@mail.de"}'),
(46, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '22', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"22","givenname":"Samuel","familyname":"Parisian","email":"Samuel.Parisian@mail.de"}'),
(47, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '23', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"23","givenname":"Irish","familyname":"Grant","email":"Irish.Grant@mail.de"}'),
(48, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '24', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"24","givenname":"Donnell","familyname":"Olson","email":"Donnell.Olson@mail.de"}'),
(49, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '25', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"25","givenname":"Jeana","familyname":"MacGyver","email":"Jeana.MacGyver@mail.de"}'),
(50, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '26', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"26","givenname":"Mark","familyname":"Stroman","email":"Mark.Stroman@mail.de"}'),
(51, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '27', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"27","givenname":"Genevive","familyname":"Christiansen","email":"Genevive.Christiansen@mail.de"}'),
(52, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '28', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"28","givenname":"Marshall","familyname":"Wisoky","email":"Marshall.Wisoky@mail.de"}'),
(53, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '29', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"29","givenname":"Hugo","familyname":"Krajcik","email":"Hugo.Krajcik@mail.de"}'),
(54, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '30', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"30","givenname":"Genevive","familyname":"Weber","email":"Genevive.Weber@mail.de"}'),
(55, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '31', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"31","givenname":"Cleotilde","familyname":"Baumbach","email":"Cleotilde.Baumbach@mail.de"}'),
(56, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '32', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"32","givenname":"Kaitlyn","familyname":"Bins","email":"Kaitlyn.Bins@mail.de"}'),
(57, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '33', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"33","givenname":"Sergio","familyname":"Hettinger","email":"Sergio.Hettinger@mail.de"}'),
(58, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '34', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"34","givenname":"Beckie","familyname":"Heaney","email":"Beckie.Heaney@mail.de"}'),
(59, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '35', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"35","givenname":"Kareem","familyname":"Considine","email":"Kareem.Considine@mail.de"}'),
(60, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '36', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"36","givenname":"Kenyatta","familyname":"Wolff","email":"Kenyatta.Wolff@mail.de"}'),
(61, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '37', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"37","givenname":"Brock","familyname":"Romaguera","email":"Brock.Romaguera@mail.de"}'),
(62, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '38', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"38","givenname":"Carey","familyname":"Reynolds","email":"Carey.Reynolds@mail.de"}'),
(63, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '39', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"39","givenname":"Gabriel","familyname":"Rath","email":"Gabriel.Rath@mail.de"}'),
(64, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '40', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"40","givenname":"Tristan","familyname":"Dare","email":"Tristan.Dare@mail.de"}'),
(65, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '41', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"41","givenname":"Augustus","familyname":"Reilly","email":"Augustus.Reilly@mail.de"}'),
(66, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '42', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"42","givenname":"Dwayne","familyname":"Lynch","email":"Dwayne.Lynch@mail.de"}'),
(67, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '43', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"43","givenname":"Ben","familyname":"Corkery","email":"Ben.Corkery@mail.de"}'),
(68, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '44', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"44","givenname":"Alfredo","familyname":"Towne","email":"Alfredo.Towne@mail.de"}'),
(69, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '45', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"45","givenname":"Shante","familyname":"Becker","email":"Shante.Becker@mail.de"}'),
(70, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '46', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"46","givenname":"Elinor","familyname":"Bernier","email":"Elinor.Bernier@mail.de"}'),
(71, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '47', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"47","givenname":"Lee","familyname":"Dibbert","email":"Lee.Dibbert@mail.de"}'),
(72, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '48', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"48","givenname":"Augustina","familyname":"Friesen","email":"Augustina.Friesen@mail.de"}'),
(73, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '49', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"49","givenname":"Kanesha","familyname":"Olson","email":"Kanesha.Olson@mail.de"}'),
(74, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '50', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"50","givenname":"Era","familyname":"Willms","email":"Era.Willms@mail.de"}');

View File

@ -0,0 +1,36 @@
/*Content*/
.content-heading {
margin-bottom: 15px;
}
/*Buttons*/
.edit {
font-size: 20px;
}
.btn-bar {
align-self: start;
}
/*Badges*/
.badge-pill {
align-self: start;
margin-right: 10px;
}
.members .badge {
align-self: start;
margin-top: 2px;
}
/*Member List*/
.members {
overflow-y: auto;
max-height: calc(100vh - 250px);
}
.members li span {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}

View File

@ -0,0 +1,31 @@
/*Grouplist Hover Effect*/
.content {
padding-right: 20px;
background: #e6f4ff;
box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .075);
}
.content:hover {
padding-right: 10px;
background: #d4ecff;
box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .25);
}
.content:hover .content-heading {
padding-bottom: 10px;
padding-left: 10px;
}
.content-text-in {
box-shadow: inset 0 .125rem .25rem rgba(0, 0, 0, .075);
}
.content:hover .content-text-in {
box-shadow: inset 0 .125rem .25rem rgba(0, 0, 0, .25);
}
/*Badges*/
.badge-pill {
margin-left: 10px;
align-self: start;
}

View File

@ -0,0 +1,27 @@
/*Grouplist*/
.content.top {
padding: 10px 10px !important;
margin-bottom: 15px !important;
}
.content {
margin-top: 5px;
padding: 5px 5px;
}
.content-heading {
margin-bottom: 5px;
}
.content-text-in {
box-shadow: none;
margin-bottom: 0;
padding: 5px;
}
/*Badges*/
.badge-pill {
align-self: start;
margin-top: 2px;
margin-right: 5px;
}

View File

@ -0,0 +1,104 @@
/*Background Light Blue: #e6f4ff*/
/* Focused: #d4ecff*/
/*Background Matt White: whitesmoke*/
/*Text Heading Blue: dodgerblue*/
/* Focused: #4fa4ff*/
/*Badge Background Private: darkslategray*/
/*Badge Background Public: #52a1eb*/
/*Badge Background Veranstaltung: lightseagreen*/
/*Badge Background Parent: mediumorchid*/
/*Content Container*/
.content {
border-radius: 5px;
background: #d4ecff;
padding: 10px 10px;
margin-bottom: 15px;
box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .25);
transition: 100ms;
}
/*Content Heading*/
.content-heading {
margin-bottom: 10px;
font-weight: bold;
font-optical-sizing: auto;
overflow-wrap: break-word;
transition: 100ms;
}
.content-heading .link {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/*Content Paragraph*/
.content-text {
margin-bottom: 15px;
cursor: default;
transition: 100ms;
}
.content-text-in {
background: whitesmoke;
border-radius: 5px;
padding: 10px;
margin-bottom: 15px;
cursor: default;
box-shadow: inset 0 .125rem .25rem rgba(0, 0, 0, .25);
transition: 100ms;
}
.content-text-in div {
overflow-wrap: break-word;
font-optical-sizing: auto;
}
/*Link*/
.link {
color: var(--primary);
transition: 100ms;
}
.link:hover {
color: #4fa4ff;
}
/*Badges*/
.badge-pill {
cursor: default;
color: whitesmoke;
}
.lecture {
background: var(--info);
}
.public {
background: var(--primary);
}
.private {
background: var(--secondary);
}
.parent {
background: var(--warning);
}
/*Grid System*/
.row {
margin-left: 0;
margin-right: 0;
}
.col {
padding-left: 0;
padding-right: 0;
}
/*Miscellanous*/
.def-cursor {
cursor: default;
}

View File

@ -0,0 +1,18 @@
// Add the following code if you want the name of the file appear on select
function disable(id) {
$(id).prop('disabled', true);
}
function enable(id) {
$(id).prop('disabled', false);
}
function copyLink() {
const copyText = document.getElementById("groupLink");
copyText.select();
copyText.setSelectionRange(0, 99999);
document.execCommand("copy");
}

View File

@ -1,71 +0,0 @@
<!DOCTYPE html>
<html lang="en"
th:replace="~{mopslayout :: html(name='Gruppenbildung', headcontent=~{:: headcontent}, navigation=~{:: navigation}, bodycontent=~{:: bodycontent})}"
xmlns="http://www.w3.org/1999/html"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title>Gruppenerstellung</title>
<th:block th:fragment="headcontent">
<!-- Links, Skripts, Styles hier einfügen! -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
</th:block>
</head>
<body>
<header>
<nav class="navigation navigation-secondary" is="mops-navigation" th:fragment="navigation"
th:switch="${account.getRoles().contains('orga')}">
<ul>
<li class="active">
<a href="/" th:href="@{/gruppen2}">Gruppen</a>
</li>
<li th:case="${true}">
<a href="/create/orga" th:href="@{/gruppen2/create/orga}">Erstellen</a>
</li>
<li th:case="${false}">
<a href="/create/student" th:href="@{/gruppen2/create/student}">Erstellen</a>
</li>
<li>
<a href="/search" th:href="@{/gruppen2/search}">Suche</a>
</li>
</ul>
</nav>
</header>
<main th:fragment="bodycontent">
<div class="container-fluid">
<div class="row">
<div class="col-10">
<h1>Metadaten ändern</h1>
<form method="post" th:action="@{/gruppen2/details/{id}/meta/update(id=${group.getId()})}">
<div class="shadow-sm p-2"
style=" border: 10px solid aliceblue; background: aliceblue;">
<div class="form-group">
<label for="title">Titel</label>
<input class="form-control" id="title" required
th:name="title" th:value="${group.getTitle()}" type="text">
</div>
<div class="form-group">
<label for="description">Beschreibung</label>
<textarea class="form-control" id="description" required rows="3"
th:name="description"
th:text="${group.getDescription()}"></textarea>
</div>
<div class="form-group pt-4">
<button class="btn btn-primary"
style="background: #52a1eb; border-style: none;"
th:if="${group.getRoles().get(user.getId()) == admin}"
type="submit">Speichern
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</main>
</body>
</html>

View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="de" xmlns:th="http://www.thymeleaf.org" th:replace="~{mopslayout :: html(
name='Gruppenbildung',
title='Neue Gruppe',
headcontent=~{fragments/general :: headcontent('none')},
navigation=~{fragments/general :: nav('create')},
bodycontent=~{:: bodycontent})}">
<body>
<main th:fragment="bodycontent">
<div class="container">
<h1 class="def-cursor">Neue Gruppe</h1>
<form enctype="multipart/form-data" method="post" th:action="@{/gruppen2/create}">
<div class="content">
<h3 class="def-cursor">Eigenschaften:</h3>
<!--Titel + Beschreibung-->
<div class="content-text" th:insert="~{fragments/forms :: meta}"></div>
<!--TODO: Enter AsciiDoc Description-->
<div class="content-text-in">
<!--Gruppentyp-->
<div th:replace="~{fragments/forms :: grouptype}"></div>
<!--Benutzerlimit-->
<div th:replace="~{fragments/forms :: userlimit}"></div>
</div>
<!--CSV Import-->
<div class="content-text mb-0" th:insert="~{fragments/forms :: csvimport}"></div>
</div>
<div class="content">
<!--Submit-->
<button class="btn btn-primary btn-block" type="submit">Gruppe Erstellen</button>
</div>
</form>
</div>
</main>
</body>
</html>

View File

@ -1,149 +0,0 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
th:replace="~{mopslayout :: html(name='Gruppenbildung', headcontent=~{:: headcontent}, navigation=~{:: navigation}, bodycontent=~{:: bodycontent})}"
xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="utf-8">
<title>Gruppenerstellung</title>
<th:block th:fragment="headcontent">
<!-- Links, Skripts, Styles hier einfügen! -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
</th:block>
</head>
<body>
<header>
<nav class="navigation navigation-secondary" is="mops-navigation" th:fragment="navigation" th:switch="${account.getRoles().contains('orga')}">
<ul>
<li>
<a th:href="@{/gruppen2}" href="/">Gruppen</a>
</li>
<li th:case="${true}" class="active">
<a href="/create/orga" th:href="@{/gruppen2/create/orga}">Erstellen</a>
</li>
<li th:case="${false}" class="active">
<a href="/create/student" th:href="@{/gruppen2/create/student}">Erstellen</a>
</li>
<li>
<a th:href="@{/gruppen2/search}" href="/search">Suche</a>
</li>
</ul>
</nav>
</header>
<main th:fragment="bodycontent">
<div class="container-fluid">
<div class="row">
<div class="col-10">
<h1>Gruppenerstellung</h1>
<form enctype="multipart/form-data" method="post" th:action="@{/gruppen2/create/orga}">
<div class="shadow-sm p-2" style=" border: 10px solid aliceblue; background: aliceblue;">
<div class="form-group">
<label for="titel">Titel</label>
<input class="form-control" id="titel" required th:name="title" type="text">
</div>
<div class="form-group">
<label for="description">Beschreibung</label>
<textarea class="form-control" id="description" required rows="3" th:name="description"></textarea>
</div>
<div class="custom-control custom-checkbox">
<!--DUMMY-->
<input type="hidden" id="maxInfiniteUsersDummy" name="maxInfiniteUsers" value="0"/>
<input class="custom-control-input" type="checkbox" id="maxInfiniteUsers" onchange="$('#maxInfiniteUsersDummy').val(this.checked ? 1 : 0)"/>
<label class="custom-control-label" for="maxInfiniteUsers">Anzahl
unbegrenzt</label>
</div>
<div class="form-group mt-3" id="userMaximum">
<label for="userMax">Teilnehmeranzahl</label>
<input class="form-control" id="userMax" th:name="userMaximum" type="number" min="1" max="100000" value="1">
</div>
<div class="custom-control custom-checkbox" id="privateCheckbox">
<!--DUMMY-->
<input type="hidden" id="visibilityDummy" name="visibility" value="0"/>
<input class="custom-control-input" type="checkbox" id="visibility" onchange="$('#visibilityDummy').val(this.checked ? 1 : 0)"/>
<label class="custom-control-label" for="visibility">Privat</label>
</div>
<div class="custom-control custom-checkbox" id="lectureCheckbox">
<!--DUMMY-->
<input type="hidden" id="lectureDummy" name="lecture" value="0"/>
<input class="custom-control-input" type="checkbox" id="lecture" onchange="$('#lectureDummy').val(this.checked ? 1 : 0)"/>
<label class="custom-control-label" for="lecture">Veranstaltung</label>
</div>
<div class="form-group mt-3" id="lectureParent">
<label for="parent">Veranstaltungszugehörigkeit</label>
<select class="form-control" id="parent" th:name="parent">
<option value="" selected>--Keine--</option>
<option th:each="lecture : ${lectures}" name="parent" th:value="${lecture.getId()}" th:text="${lecture.getTitle()}"></option>
</select>
</div>
<div class="form-group pt-4">
<div class="row">
<div class="col">
<div class="custom-file">
<label class="custom-file-label" for="file">CSV Datei von
Mitgliedern
hochladen</label>
<input class="custom-file-input" id="file" th:name="file" type="file">
</div>
</div>
</div>
</div>
<div class="form-group pt-4">
<button class="btn btn-primary"
style="background: #52a1eb; border-style: none;"
type="submit">Erstellen
</button>
</div>
</div>
</form>
</div>
</div>
</div>
<script>
// Add the following code if you want the name of the file appear on select
$(".custom-file-input").on("change", function () {
const fileName = $(this).val().split("\\").pop();
$(this).siblings(".custom-file-label").addClass("selected").html(fileName);
});
// Collapse lectureParent if lecture
$(document).ready(function () {
$('#lecture').change(function () {
$('#parent').prop('disable', function (i, v) { return !v; });
});
});
// Collapse provateCheckbox if lecture
$(document).ready(function () {
$('#lecture').change(function () {
$('#visibility').prop('disabled', function (i, v) { return !v; });
});
});
// Collapse lectureCheckbox if private
$(document).ready(function () {
$('#visibility').change(function () {
$('#lecture').prop('disabled', function (i, v) { return !v; });
});
});
// Collapse userMaximum if infinite
$(document).ready(function () {
$('#maxInfiniteUsers').change(function () {
$('#userMax').prop('readonly', function (i, v) { return !v; });
});
});
</script>
</main>
</body>
</html>

View File

@ -1,98 +0,0 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
th:replace="~{mopslayout :: html(name='Gruppenbildung', headcontent=~{:: headcontent}, navigation=~{:: navigation}, bodycontent=~{:: bodycontent})}"
xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="utf-8">
<title>Gruppenerstellung</title>
<th:block th:fragment="headcontent">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
</th:block>
</head>
<body>
<header>
<nav class="navigation navigation-secondary" is="mops-navigation" th:fragment="navigation" th:switch="${account.getRoles().contains('orga')}">
<ul>
<li>
<a th:href="@{/gruppen2}" href="/">Gruppen</a>
</li>
<li th:case="${true}" class="active">
<a href="/create/orga" th:href="@{/gruppen2/create/orga}">Erstellen</a>
</li>
<li th:case="${false}" class="active">
<a href="/create/student" th:href="@{/gruppen2/create/student}">Erstellen</a>
</li>
<li>
<a th:href="@{/gruppen2/search}" href="/search">Suche</a>
</li>
</ul>
</nav>
</header>
<main th:fragment="bodycontent">
<div class="container-fluid">
<div class="row">
<div class="col-10">
<h1>Gruppenerstellung</h1>
<form method="post" th:action="@{/gruppen2/create/student}">
<div class="shadow-sm p-2"
style=" border: 10px solid aliceblue; border-radius: 5px; background: aliceblue;">
<div class="form-group">
<label for="titel">Titel</label>
<input class="form-control" id="titel" required th:name="title" type="text">
</div>
<div class="form-group">
<label for="description">Beschreibung</label>
<textarea class="form-control" id="description" required rows="3" th:name="description"></textarea>
</div>
<div class="custom-control custom-checkbox">
<!--DUMMY-->
<input type="hidden" id="maxInfiniteUsersDummy" name="maxInfiniteUsers" value="0"/>
<input class="custom-control-input" type="checkbox" id="maxInfiniteUsers" onchange="$('#maxInfiniteUsersDummy').val(this.checked ? 1 : 0)"/>
<label class="custom-control-label" for="maxInfiniteUsers">Anzahl
unbegrenzt</label>
</div>
<div class="form-group mt-3" id="userMaximum">
<label for="userMax">Teilnehmeranzahl</label>
<input class="form-control" th:name="userMaximum"
type="number" min="1" max="10000" id="userMax">
</div>
<div class="custom-control custom-checkbox">
<!--DUMMY-->
<input type="hidden" id="visibilityDummy" name="visibility" value="0"/>
<input class="custom-control-input" type="checkbox" id="visibility" onchange="$('#visibilityDummy').val(this.checked ? 1 : 0)"/>
<label class="custom-control-label" for="visibility">Privat</label>
</div>
<div class="form-group" id="lectureParent">
<label for="parent">Veranstaltungszugehörigkeit</label>
<select class="form-control" id="parent" name="parent">
<option value="" selected>--Keine--</option>
<option th:each="lecture : ${lectures}" th:name="parent" th:value="${lecture.getId()}" th:text="${lecture.getTitle()}">
</option>
</select>
</div>
<div class="form-group pt-4">
<button class="btn btn-primary"
style="background: #52a1eb; border-style: none;"
type="submit">Erstellen
</button>
</div>
</div>
</form>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('#maxInfiniteUsers').change(function () {
$('#userMaximum').fadeToggle();
});
});
</script>
</main>
</body>
</html>

View File

@ -0,0 +1,70 @@
<!DOCTYPE html>
<html lang="de" xmlns:th="http://www.thymeleaf.org" th:replace="~{mopslayout :: html(
name='Gruppenbildung',
title='Details',
headcontent=~{fragments/general :: headcontent('details')},
navigation=~{fragments/general :: nav('none')},
bodycontent=~{:: bodycontent})}">
<body>
<main th:fragment="bodycontent">
<div class="container-fluid">
<h1 class="def-cursor" th:text="${group.getTitle()}"></h1>
<div class="row">
<!--Gruppendetails-->
<div class="col-9 px-0">
<div class="content" th:insert="~{fragments/groups :: groupcontent}"></div>
<div class="content">
<!--Button-Bar-->
<div class="row">
<a class="btn btn-primary" href="/gruppen2">Fertig</a>
<!--Spacer-->
<span class="col"></span>
<form method="post" th:action="@{/gruppen2/details/{id}/leave(id=${group.getId()})}">
<button class="btn btn-danger btn-bar" type="submit">Gruppe verlassen
</button>
</form>
</div>
</div>
</div>
<!--Teilnehmerliste-->
<div class="col-3 def-cursor">
<!--Anzahl Text-->
<div class="mb-2">
<span>Teilnehmer: </span>
<span th:text="${group.getMembers().size() + ' von ' + group.getUserLimit()}"></span>
</div>
<!--Bearbeiten-Button-->
<div class="mb-2" th:if="${group.getRoles().get(user.getId()) == admin}">
<form method="get"
th:action="@{/gruppen2/details/{id}/edit(id=${group.getId()})}">
<button class="btn btn-secondary btn-block">Gruppe verwalten</button>
</form>
</div>
<!--Liste-->
<div class="members">
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between"
th:each="member : ${group.getMembers()}">
<span th:text="${member.getGivenname() + ' ' + member.getFamilyname().charAt(0) + '.'}"></span>
<span th:replace="~{fragments/groups :: userbadges}"></span>
</li>
</ul>
</div>
</div>
</div>
</div>
</main>
</body>
</html>

View File

@ -1,148 +0,0 @@
<!DOCTYPE html>
<html lang="en"
th:replace="~{mopslayout :: html(name='Gruppenbildung', headcontent=~{:: headcontent}, navigation=~{:: navigation}, bodycontent=~{:: bodycontent})}"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title>Gruppendetails</title>
<th:block th:fragment="headcontent">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
rel="stylesheet">
</th:block>
</head>
<body>
<header>
<nav class="navigation navigation-secondary" is="mops-navigation" th:fragment="navigation" th:switch="${account.getRoles().contains('orga')}">
<ul>
<li class="active">
<a href="/" th:href="@{/gruppen2}">Gruppen</a>
</li>
<li th:case="${true}">
<a href="/create/orga" th:href="@{/gruppen2/create/orga}">Erstellen</a>
</li>
<li th:case="${false}">
<a href="/create/student" th:href="@{/gruppen2/create/student}">Erstellen</a>
</li>
<li>
<a href="/search" th:href="@{/gruppen2/search}">Suche</a>
</li>
</ul>
</nav>
</header>
<main th:fragment="bodycontent">
<div class="container-fluid">
<div>
<div class="shadow-sm p-4 col-8"
style="border: 10px solid aliceblue; display: inline-block; border-radius: 5px; background: aliceblue;">
<div class="row">
<div class="col-11">
<h1 style="color: black; font-weight: bold; font-optical-sizing: auto; overflow-wrap: break-word; width: 95%;"
th:text="${group.getTitle()}"></h1>
</div>
<div class="col-1">
<a class="fa fa-pencil"
style="font-size:30px; width: 5%;"
th:href="@{/gruppen2/details/{id}/meta(id=${group.getId()})}"
th:if="${group.getRoles().get(user.getId()) == admin}"></a>
</div>
</div>
<h3>
<span class="badge badge-pill badge-dark" style="background: darkslategray;"
th:if='${group.getVisibility() == group.getVisibility().PRIVATE }'>Private Gruppe</span>
<span class="badge badge-pill badge-primary" style="background: #52a1eb;"
th:if="${group.getVisibility() == group.getVisibility().PUBLIC}">Öffentliche Gruppe</span>
<span class="badge badge-pill badge-success" style="background: lightseagreen;"
th:if='${group.getType() == group.getType().LECTURE}'>Veranstaltung</span>
<span class="badge badge-pill badge-info" style="background: mediumorchid;"
th:text="${parent?.getTitle()}">Parent</span>
<div class="input-group mb-3" style="margin-top: 10px;"
th:if="${group.getRoles().get(user.getId()) == admin}">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default"
style="background: #52a1eb;">Einladungslink:</span>
</div>
<input aria-describedby="basic-addon2" aria-label="Recipient's username"
class="form-control"
id="groupLink" readonly style="background: white;" th:value="${link}"
type="text">
<div class="input-group-append">
<button class="btn btn-outline-secondary" onclick="copyLink()"
type="button">Link kopieren
</button>
</div>
</div>
</h3>
<br>
<div class="shadow-sm p-4" style="background: white;">
<p style="overflow-wrap: break-word; font-optical-sizing: auto;" th:text="${group.getDescription()}"></p>
</div>
<br>
<div class="text-right btn-toolbar" role="toolbar" style="float: right;">
<button class="btn btn-primary"
style="background: #52a1eb; border: none; margin: 5px;">
<a style="color: white;" th:href="@{/gruppen2}">Zurück</a>
</button>
<form method="post" th:action="@{/gruppen2/details/{id}/leave(id=${group.getId()})}">
<button class="btn btn-danger" style="border-style: none; margin: 5px;"
th:name="group_id"
type="submit">Gruppe verlassen
</button>
</form>
<form method="post" th:action="@{/gruppen2/details/{id}/destroy(id=${group.getId()})}">
<button class="btn btn-danger" style="border-style: none; margin: 5px;" th:name="group_id"
th:if="${group.getRoles().get(user.getId()) == admin}"
type="submit">Gruppe löschen
</button>
</form>
</div>
</div>
<div class="col-4" style="white-space: nowrap; float: right; background: white; display: inline-block; margin-bottom: 100px; margin-top: -8px;">
<h2>Mitglieder</h2>
<div th:switch="${group.getUserLimit() != 100000}">
<h4 th:case="${true}">
<a th:text="${group.getMembers().size()}"></a>
<a>von maximal</a>
<a th:text="${group.getUserLimit()}"></a>
<a>Benutzern.</a>
</h4>
<h4 th:case="${false}"> unbegrenzte Teilnehmeranzahl</h4>
</div>
<div th:if="${group.getRoles().get(user.getId()) == admin}">
<form method="get"
th:action="@{/gruppen2/details/{id}/members(id=${group.getId()})}">
<button class="btn btn-secondary" style="background: slategrey; float: left;">
Mitglieder bearbeiten
</button>
</form>
<br>
<br>
</div>
<div style="overflow-y: scroll;
height:60vh;">
<ul class="list-group-flush" style="background: slategrey;"
th:each="member : ${group.getMembers()}">
<li class="list-group-item" style="background: aliceblue;">
<span th:if='${group.getVisibility() == public}' th:text="${member.getId()}"></span>
<span th:if='${group.getVisibility() == private}' th:text="${#strings.abbreviate(member.getGivenname(), 15) + ' ' + member.getFamilyname().substring(0, 1) + '.'}"></span>
<span class="badge badge-success"
th:if='${group.getRoles().get(member.getId()) == admin}'>admin</span>
</li>
</ul>
</div>
</div>
</div>
</div>
<script>
function copyLink() {
const copyText = document.getElementById("groupLink");
copyText.select();
copyText.setSelectionRange(0, 99999);
document.execCommand("copy");
}
</script>
</main>
</body>
</html>

View File

@ -1,57 +0,0 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
th:replace="~{mopslayout :: html(name='Gruppenbildung', headcontent=~{:: headcontent}, navigation=~{:: navigation}, bodycontent=~{:: bodycontent})}">
<head>
<meta charset="utf-8">
<title>Gruppendetails</title>
<th:block th:fragment="headcontent">
<!-- Links, Skripts, Styles hier einfügen! -->
</th:block>
</head>
<body>
<header>
<nav class="navigation navigation-secondary" is="mops-navigation" th:fragment="navigation" th:switch="${account.getRoles().contains('orga')}">
<ul>
<li>
<a th:href="@{/gruppen2}" href="/">Gruppen</a>
</li>
<li th:case="${true}">
<a href="/create/orga" th:href="@{/gruppen2/create/orga}">Erstellen</a>
</li>
<li th:case="${false}">
<a href="/create/student" th:href="@{/gruppen2/create/student}">Erstellen</a>
</li>
<li class="active">
<a th:href="@{/gruppen2/search}" href="/search">Suche</a>
</li>
</ul>
</nav>
</header>
<main th:fragment="bodycontent">
<div class="container-fluid">
<div class="shadow-sm p-4" style="border: 1px solid aliceblue; border-radius: 5px; background: aliceblue;">
<h1 style="color: black; font-weight: bold; font-optical-sizing: auto; overflow-wrap: break-word;" th:text="${group.getTitle()}"></h1>
<h3>
<span class="badge badge-pill badge-dark" style="background: darkslategray;"
th:if='${group.getVisibility() == group.getVisibility().PRIVATE }'>Private Gruppe</span>
<span class="badge badge-pill badge-primary" style="background: #52a1eb;"
th:if="${group.getVisibility() == group.getVisibility().PUBLIC}">Öffentliche Gruppe</span>
<span class="badge badge-pill badge-success"
style="background: lightseagreen;"
th:if='${group.getType() == lecture}'> Veranstaltung</span>
<span class="badge badge-pill badge-info" style="background: mediumorchid;"
th:text="${parent?.getTitle()}">Parent</span>
</h3>
<div class="shadow-sm p-4" style="background: white;">
<p style="overflow-wrap: break-word; font-optical-sizing: auto;"
th:text="${group.getDescription()}"></p>
</div>
<form class="mt-3" th:if="${group.getVisibility() == public}" method="post"
th:action="@{/gruppen2/details/{id}/join(id=${group.getId()})}">
<button class="btn btn-primary" type="submit">Beitreten!</button>
</form>
</div>
</div>
</main>
</body>
</html>

View File

@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang="de" xmlns:th="http://www.thymeleaf.org" th:replace="~{mopslayout :: html(
name='Gruppenbildung',
title='Gruppe verwalten',
headcontent=~{fragments/general :: headcontent('none')},
navigation=~{fragments/general :: nav('none')},
bodycontent=~{:: bodycontent})}">
<body>
<main th:fragment="bodycontent">
<div class="container-fluid">
<h1 class="def-cursor" th:text="${group.getTitle()}"></h1>
<!--Fertig oder löschen-->
<div class="content">
<div class="row">
<form method="get" th:action="@{/gruppen2/details/{id}(id=${group.getId()})}">
<button class="btn btn-primary">Fertig</button>
</form>
<!--Spacer-->
<span class="col"></span>
<form method="post" th:action="@{/gruppen2/details/{id}/edit/destroy(id=${group.getId()})}">
<button class="btn btn-danger btn-bar" type="submit">Gruppe löschen
</button>
</form>
</div>
</div>
<!--Invite Link-->
<div class="content input-group">
<div class="input-group-prepend">
<span class="input-group-text">Einladungslink:</span>
</div>
<input class="form-control" readonly th:value="${link}" type="text">
<div class="input-group-append">
<button type="button" class="btn btn-secondary"
onclick="copyLink()">Link kopieren
</button>
</div>
</div>
<!--Meta-->
<div class="content">
<div class="content-heading">
<span>Eigenschaften</span>
</div>
<!--Beschreibung + Titel-->
<div class="content-text">
<form th:action="@{/gruppen2/details/{id}/edit/meta(id=${group.getId()})}"
method="post">
<div th:replace="~{fragments/forms :: meta}"></div>
<div class="row">
<span class="col"></span>
<button type="submit" class="btn btn-secondary mt-2">Speichern</button>
</div>
</form>
</div>
<!--Userlimit-->
<div class="content-text-in">
<form th:action="@{/gruppen2/details/{id}/edit/userlimit(id=${group.getId()})}"
method="post">
<div th:replace="~{fragments/forms :: userlimit}"></div>
<div class="row">
<span class="col"></span>
<button type="submit" class="btn btn-secondary mt-2">Speichern</button>
</div>
</form>
</div>
<!--CSV Import-->
<div class="content-text mb-0">
<form th:action="@{/gruppen2/details/{id}/edit/csv(id=${group.getId()})}"
th:if="${account.getRoles().contains('orga')}"
enctype="multipart/form-data" method="post">
<div th:replace="~{fragments/forms :: csvimport}"></div>
<div class="row">
<span class="col"></span>
<button type="submit" class="btn btn-secondary mt-2">Speichern</button>
</div>
</form>
</div>
</div>
<!--Teilnehmerliste-->
<div class="content members">
<div class="content-heading">
<span>Teilnehmer</span>
</div>
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between" th:each="member : ${group.getMembers()}">
<div>
<span th:text="${member.getGivenname() + ' ' + member.getFamilyname().charAt(0) + '.'}"></span>
<span th:replace="~{fragments/groups :: userbadges}"></span>
</div>
<div class="d-flex">
<form th:action="@{/gruppen2/details/{id}/edit/delete/{userid}(id=${group.getId()}, userid=${member.getId()})}"
th:unless="${member.getId() == account.getName()}"
method="post">
<button type="submit" class="btn btn-danger mr-2">Entfernen</button>
</form>
<form th:action="@{/gruppen2/details/{id}/edit/role/{userid}(id=${group.getId()}, userid=${member.getId()})}"
method="post">
<button type="submit" class="btn btn-warning">Rolle ändern</button>
</form>
</div>
</li>
</ul>
</div>
</div>
</main>

View File

@ -1,144 +0,0 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
th:replace="~{mopslayout :: html(name='Gruppenbildung', headcontent=~{:: headcontent}, navigation=~{:: navigation}, bodycontent=~{:: bodycontent})}">
<head>
<meta charset="utf-8">
<title>Gruppendetails</title>
<th:block th:fragment="headcontent">
<!-- Links, Skripts, Styles hier einfügen! -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
</th:block>
</head>
<body>
<header>
<nav class="navigation navigation-secondary" is="mops-navigation" th:fragment="navigation" th:switch="${account.getRoles().contains('orga')}">
<ul>
<li class="active">
<a th:href="@{/gruppen2}" href="/">Gruppen</a>
</li>
<li th:case="${true}">
<a href="/create/orga" th:href="@{/gruppen2/create/orga}">Erstellen</a>
</li>
<li th:case="${false}">
<a href="/create/student" th:href="@{/gruppen2/create/student}">Erstellen</a>
</li>
<li>
<a th:href="@{/gruppen2/search}" href="/search">Suche</a>
</li>
</ul>
</nav>
</header>
<main th:fragment="bodycontent">
<div class="container-fluid">
<div class="row">
<div class="col-10">
<h1>Mitglieder bearbeiten</h1>
<!--TODO: Anzeige bei unbegrenzt-->
<div th:switch="${group.getUserLimit() != 100000}">
<h5 th:case="${true}">
<a th:text="${group.getMembers().size()}"></a>
<a>von maximal</a>
<a th:text="${group.getUserLimit()}"></a>
<a>Benutzern.</a>
</h5>
<h5 th:case="${false}"> unbegrenzte Teilnehmeranzahl</h5>
</div>
<div class="shadow p-2" style="border: 10px solid aliceblue; background: aliceblue;">
<div class="form-group pt-4" th:if="${account.getRoles().contains('orga')}">
<form th:action="@{/gruppen2/details/{id}/members/update/csv(id=${group.getId()})}"
enctype="multipart/form-data" method="post">
<div class="input-group mb-3">
<div class="custom-file">
<input class="custom-file-input" id="file" th:name="file"
type="file">
<label class="custom-file-label" for="file">CSV Datei von
Mitgliedern
hochladen</label>
</div>
<div class="input-group-append">
<button class="btn btn-outline-secondary"
style="background: #52a1eb; border-style: none;"
th:name="group_id" type="submit">
<a style="color: white;">Hinzufügen</a>
</button>
</div>
</div>
</form>
</div>
<div class="form-group pt-4">
<form method="post" th:action="@{/gruppen2/details/{id}/members/update/userlimit(id=${group.getId()})}">
<div class="input-group mb-3" id="userMaximum">
<label for="teilnehmerzahl"></label>
<input class="form-control"
id="teilnehmerzahl"
placeholder="Maximale Teilnehmerzahl ändern..."
th:name="maximum"
type="number" th:min="${group.getMembers().size()}"
max="10000">
<div class="input-group-append">
<button class="btn btn-outline-secondary"
style="background: #52a1eb; border-style: none;"
th:name="group_id" type="submit">
<a style="color: white;">Speichern</a>
</button>
</div>
</div>
</form>
</div>
<table class="table" style="table-layout: fixed;">
<thead>
<tr>
<th scope="col">Mitglied</th>
<th scope="col" style="width: 180px;">Rolle</th>
<th scope="col" style="width: 280px;">Optionen</th>
</tr>
</thead>
<tbody class="table-striped">
<tr th:each="member : ${group.getMembers()}">
<th th:text="${#strings.abbreviate(member.getGivenname(), 15) + ' ' + member.getFamilyname().substring(0, 1) + '.'}"></th>
<td>
<span th:if='${group.getRoles().get(member.getId()) != admin}'>Mitglied</span>
<span th:if='${group.getRoles().get(member.getId()) == admin}'>Admin</span>
</td>
<td>
<div class="text-right btn-toolbar" style="float: right;" role="toolbar">
<form method="post"
th:action="@{/gruppen2/details/{id}/members/{userid}/update/role(id=${group.getId()}, userid=${member.getId()})}">
<button class="btn btn-warning btn-sm" type="submit"
style="margin: 5px;">Rolle ändern
</button>
</form>
<form method="post"
th:action="@{/gruppen2/details/{id}/members/{userid}/delete(id=${group.getId()}, userid=${member.getId()})}">
<button class="btn btn-danger btn-sm" style="margin: 5px;"
th:if='!${account.getName().equals(member.getId())}'>
Mitglied entfernen
</button>
</form>
</div>
</td>
</tr>
</tbody>
</table>
<form method="get" th:action="@{/gruppen2/details/{id}(id=${group.getId()})}">
<button class="btn btn-primary" style="background: #52a1eb; border-style: none;" type="submit">
Fertig
</button>
</form>
</div>
</div>
</div>
</div>
<script>
// Add the following code if you want the name of the file appear on select
$(".custom-file-input").on("change", function () {
var fileName = $(this).val().split("\\").pop();
$(this).siblings(".custom-file-label").addClass("selected").html(fileName);
});
</script>
</main>

View File

@ -1,38 +1,37 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"> <html lang="de" xmlns:th="http://www.thymeleaf.org" th:replace="~{mopslayout :: html(
<head> name='Gruppenfindung',
<meta charset="utf-8"> title='Error',
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport"> headcontent=~{fragments/general :: headcontent('none')},
<link crossorigin="anonymous" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" navigation=~{fragments/general :: nav('none')},
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" rel="stylesheet"> bodycontent=~{:: bodycontent})}">
<meta charset="UTF-8">
<title th:text="'MOPS - ' + ${error}"></title>
</head>
<body> <body>
<div class="mx-auto" style="vertical-align: center; border-radius: 5px; horiz-align: center; top: 50%; left: 50%;">
<div class="jumbotron" style="background: aliceblue;"> <main th:fragment="bodycontent">
<div class="container">
<h1 class="display-3">UPSI</h1>
<p class="lead">Da ist wohl etwas schiefgelaufen :(</p><br>
</div>
</div>
<div class="container"> <div class="container">
<div class="card mb-4 shadow-sm">
<div class="card-header"> <h1 class="display-3 def-cursor">UPSI</h1>
<h4 class="my-0 font-weight-normal" th:text="${status} + ': ' + ${error}"></h4>
</div> <div class="content">
<div class="card-body"> <p class="lead def-cursor">Da ist wohl etwas schiefgelaufen :(</p>
<p th:text="${message}"></p>
<div class="content-text-in">
<div class="card">
<div class="card-header">
<h4 class="my-0 font-weight-normal" th:text="${status} + ': ' + ${error}"></h4>
</div>
<div class="card-body">
<p th:text="${message}"></p>
</div>
</div>
</div> </div>
</div> </div>
<button class="btn btn-primary"
style="background: #52a1eb; border-style: none;">
<a href="#" onclick="window.history.back(-1);return false;" role="button"
style="color: white;">Zurück</a>
</button>
</div> </div>
</div> </main>
</body> </body>
</html> </html>

View File

@ -0,0 +1,111 @@
<!DOCTYPE HTML>
<!--suppress ALL -->
<html lang="de" xmlns:th="http://www.thymeleaf.org">
<!--Meta-->
<th:block th:fragment="meta">
<!--Gruppentitel-->
<div class="input-group mb-2"
title="Ein Gruppentitel zwischen 4 und 128 Zeichen. Der Titel ist öffentlich.">
<div class="input-group-prepend">
<span class="input-group-text text-monospace">Gruppentitel:</span>
</div>
<input type="text" class="form-control" th:name="title" th:value="${group?.getTitle()}"
required minlength="4" maxlength="128">
</div>
<!--Gruppenbeschreibung-->
<div class="input-group"
title="Eine kurze Gruppenbeschreibung zwischen 4 und 512 Zeichen. Die Beschreibung ist öffentlich.">
<div class="input-group-prepend">
<span class="input-group-text text-monospace">Beschreibung:</span>
</div>
<textarea class="form-control" th:name="description" th:text="${group?.getDescription()}"
required minlength="4" maxlength="512"></textarea>
</div>
</th:block>
<!--Gruppentyp-->
<th:block th:fragment="grouptype">
<label for="grouptype">Gruppentyp:</label>
<div class="btn-toolbar row mb-2 mx-0" id="grouptype">
<div class="btn-group btn-group-toggle col-sm-4 px-0" data-toggle="buttons">
<label class="btn btn-secondary active" onclick="enable('#parentselect'); disable('#parentdummy')">
<input type="radio" name="type" value="PRIVATE" checked> Privat
</label>
<label class="btn btn-secondary" onclick="enable('#parentselect'); disable('#parentdummy')">
<input type="radio" name="type" value="PUBLIC"> Öffentlich
</label>
<label class="btn btn-secondary" onclick="disable('#parentselect'); enable('#parentdummy')"
th:if="${account.getRoles().contains('orga')}">
<input type="radio" name="type" value="LECTURE"> Veranstaltung
</label>
</div>
<div class="input-group col-sm-8 pr-0"
title="Optional kann eine Veranstaltungszugehörigkeit festgelegt werden.">
<div class="input-group-prepend">
<span class="input-group-text text-monospace">Gehört zu:</span>
</div>
<input type="hidden" id="parentdummy" value="00000000-0000-0000-0000-000000000000" disabled>
<select class="custom-select" id="parentselect" name="parent">
<option value="00000000-0000-0000-0000-000000000000" selected>Keiner</option>
<option th:each="lecture : ${lectures}" th:value="${lecture.getId()}"
th:text="${lecture.getTitle()}"></option>
</select>
</div>
</div>
</th:block>
<!--Userlimit-->
<th:block th:fragment="userlimit">
<label for="userlimit">Teilnehmeranzahl:</label>
<div class="btn-toolbar row mx-0" id="userlimit">
<input type="hidden" name="userlimit" id="limit" value="999999"
th:disabled="${group != null && group.getUserLimit() < 999999} ? 'disabled' : 'false'">
<div class="btn-group btn-group-toggle col-sm-4 px-0" data-toggle="buttons">
<label class="btn btn-secondary active" onclick="disable('#limitselect'); enable('#limit')">
<input type="radio"
th:checked="${group != null && group.getUserLimit() < 999999} ? 'false' : 'checked'">
Unbegrenzt
</label>
<label class="btn btn-secondary" onclick="enable('#limitselect'); disable('#limit')">
<input type="radio"
th:checked="${group != null && group.getUserLimit() < 999999} ? 'checked' : 'false'">
Begrenzt
</label>
</div>
<div class="input-group col-sm-8 pr-0"
title="999999 ist die maximal zulässige Teilnehmerzahl. Ist diese Obergrenze erreicht, gilt die Gruppe als unbegrenzt.">
<div class="input-group-prepend">
<span class="input-group-text text-monospace">Limit:</span>
</div>
<input type="number" class="form-control" name="userlimit"
th:value="${group != null} ? ${group.getUserLimit()} : '999999'"
min="1" max="999999" id="limitselect" required
th:disabled="${group != null && group.getUserLimit() < 999999} ? 'false' : 'disabled'">
<div class="input-group-append">
<span class="input-group-text text-monospace">Teilnehmer</span>
</div>
</div>
</div>
</th:block>
<!--CSV-->
<div th:fragment="csvimport" class="input-group" th:if="${account.getRoles().contains('orga')}"
title="Das CSV folgt dem Format id,givenname,familyname,email.">
<div class="input-group-prepend">
<span class="input-group-text text-monospace">CSV:</span>
</div>
<div class="custom-file">
<input type="file" class="custom-file-input" id="file" th:name="file">
<label class="custom-file-label" for="file">Datei auswählen</label>
</div>
<script>
$(".custom-file-input").on("change", function () {
const fileName = $(this).val().split("\\").pop();
$(this).siblings(".custom-file-label").addClass("selected").html(fileName);
});
</script>
</div>

View File

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<!--suppress ALL -->
<html lang="de" xmlns:th="http://www.thymeleaf.org">
<!--Stylesheets, Javascript etc.-->
<th:block th:fragment="headcontent(style)">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<script src="https://kit.fontawesome.com/22c0caaa8a.js" crossorigin="anonymous"></script>
<script type="text/javascript" th:src="@{/js/script.js}"></script>
<link th:href="@{/css/style.css}" rel="stylesheet"/>
<link th:unless="${style == 'none'}" th:href="@{'/css/' + ${style} + '.css'}" rel="stylesheet"/>
</th:block>
<!--Left navigation bar-->
<nav class="navigation navigation-secondary" is="mops-navigation" th:fragment="nav(current)">
<ul>
<li th:class="${current == 'index'} ? 'active' : ''">
<a th:href="@{/gruppen2}">Gruppen</a>
</li>
<li th:class="${current == 'create'} ? 'active' : ''">
<a th:href="@{/gruppen2/create}">Erstellen</a>
</li>
<li th:class="${current == 'search'} ? 'active' : ''">
<a th:href="@{/gruppen2/search}">Suchen</a>
</li>
</ul>
</nav>
</html>

View File

@ -0,0 +1,70 @@
<!DOCTYPE HTML>
<!--suppress ALL -->
<html lang="de" xmlns:th="http://www.thymeleaf.org">
<!--Grouptype Badges-->
<th:block th:fragment="badges">
<span class="badge badge-pill private"
title="Kann nicht über die Suche gefunden werden, beitritt ist per Einladungslink möglich."
th:if='${group.getType() == private}'>Privat</span>
<span class="badge badge-pill public"
title="Kann über die Suche gefunden werden, jeder kann beitreten."
th:if="${group.getType() == public}">Öffentlich</span>
<span class="badge badge-pill lecture"
title="Offizielle Veranstaltung"
th:if='${group.getType() == lecture}'>Veranstaltung</span>
<span class="badge badge-pill parent"
th:unless="${parent == null || parent.getTitle() == null|| parent.getTitle() == ''}"
th:title="${'Die Gruppe gehört zur Veranstaltung ' + parent.getTitle() + '.'}"
th:text="${parent.getTitle()}">Parent</span>
<!--Needs completly projected (cached) groups-->
<!--<span class="badge badge-success float-right"
th:if='${group.getRoles().get(user.getId()) == admin}'>Admin</span>-->
</th:block>
<!--User Badges-->
<th:block th:fragment="userbadges">
<span class="badge badge-success align-self-start ml-2"
th:if="${group.getRoles().get(member.getId()) == admin}">Admin</span>
</th:block>
<th:block th:fragment="groupcontent">
<!--Badges-->
<div class="content-heading">
<span th:replace="~{fragments/groups :: badges}"></span>
</div>
<!--Description-->
<div class="content-text-in">
<span th:text="${group.getDescription()}"></span>
</div>
<!--<div class="content-text-in" th:if="${group.getMembers().contains(user.getId())}"></div>-->
</th:block>
<!--Buttonbar zum Gruppe beitreten-->
<th:block th:fragment="joingroup">
<div class="content-heading">
<span th:if="${group.getMembers().size() < group.getUserLimit()}">
Möchtest du dieser Gruppe beitreten?
</span>
<span th:unless="${group.getMembers().size() < group.getUserLimit()}">
Diese Gruppe hat ihre maximale Teilnehmeranzahl erreicht.
</span>
</div>
<div class="row">
<form method="post" th:action="@{/gruppen2/details/{id}/join(id = ${group.getId()})}"
th:if="${group.getMembers().size() < group.getUserLimit()}">
<button class="btn btn-success" type="submit">Gruppe beitreten.</button>
</form>
<div class="col" th:if="${group.getMembers().size() < group.getUserLimit()}"></div>
<a class="btn btn-primary" href="/gruppen2"
type="submit">Startseite.</a>
</div>
</th:block>

View File

@ -1,70 +1,33 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" <html lang="de" xmlns:th="http://www.thymeleaf.org" th:replace="~{mopslayout :: html(
th:replace="~{mopslayout :: html(name='Gruppenbildung', headcontent=~{:: headcontent}, navigation=~{:: navigation}, bodycontent=~{:: bodycontent})}"> name='Gruppenfindung',
<head> title='Meine Gruppen',
<meta charset="utf-8"> headcontent=~{fragments/general :: headcontent('index')},
<title>Eigene Gruppen</title> navigation=~{fragments/general :: nav('index')},
<th:block th:fragment="headcontent"> bodycontent=~{:: bodycontent})}">
<!-- Links, Skripts, Styles hier einfügen! -->
</th:block>
</head>
<body> <body>
<header>
<nav class="navigation navigation-secondary" is="mops-navigation" th:fragment="navigation"
th:switch="${account.getRoles().contains('orga')}">
<ul>
<li class="active">
<a href="/" th:href="@{/gruppen2}">Gruppen</a>
</li>
<li th:case="${true}">
<a href="/create/orga" th:href="@{/gruppen2/create/orga}">Erstellen</a>
</li>
<li th:case="${false}">
<a href="/create/student" th:href="@{/gruppen2/create/student}">Erstellen</a>
</li>
<li>
<a href="/search" th:href="@{/gruppen2/search}">Suche</a>
</li>
</ul>
</nav>
</header>
<main th:fragment="bodycontent"> <main th:fragment="bodycontent">
<div class="container-fluid"> <div class="container-fluid">
<div class="row">
<div class="col-10"> <h1 class="def-cursor">Meine Gruppen</h1>
<h1>Meine Gruppen</h1>
<form method="get" th:action="@{/}"> <!--Gruppenliste belegte Gruppen-->
<h3 style="color: dodgerblue; font-weight: bold; font-optical-sizing: auto;"> <div class="content" th:each="group: ${groups}">
<small style="font-weight: normal; color: black;">Mitglied in </small> <div class="content-heading row">
<small style="font-weight: bold; color: black;" <a class="link col" th:href="@{/gruppen2/details/{id}(id=${group.getId()})}"
th:text="${gruppen.size()}"></small> th:text="${group.getTitle()}"></a>
<small style="font-weight: normal; color: black;"
th:if='${gruppen.size()==1}'> Gruppe.</small> <span th:replace="~{fragments/groups :: badges}"></span>
<small style="font-weight: normal; color: black;" </div>
th:if='${gruppen.size()!=1}'> Gruppen.</small> <div class="content-text-in">
</h3> <span th:text="${group.getDescription()}"></span>
<br>
<div th:each="gruppe: ${gruppen}">
<div class="shadow-sm p-4"
style="border: none; border-radius: 5px; background: aliceblue;">
<h3 style="color: dodgerblue; font-weight: bold; font-optical-sizing: auto; overflow-wrap: break-word;">
<span class="badge badge-pill badge-success"
style="background: lightseagreen; margin-right: 25px; float: right;"
th:if='${gruppe.getType() == lecture}'>Veranstaltung</span>
<a th:href="@{/gruppen2/details/{id}(id=${gruppe.getId()})}"
th:text="${gruppe.getTitle()}"></a>
</h3>
<div class="shadow-sm p-4" style="background: white;">
<p style="overflow-wrap: break-word; font-optical-sizing: auto;"
th:text="${#strings.abbreviate(gruppe.getDescription(),300)}"></p>
</div>
</div>
<br>
</div>
</form>
</div> </div>
</div> </div>
</div> </div>
</main> </main>
</body> </body>
</html> </html>

View File

@ -1,70 +0,0 @@
<!DOCTYPE html>
<html lang="en"
th:replace="~{mopslayout :: html(name='Gruppenbildung', headcontent=~{:: headcontent}, navigation=~{:: navigation}, bodycontent=~{:: bodycontent})}"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title>Gruppendetails</title>
<th:block th:fragment="headcontent">
<!-- Links, Skripts, Styles hier einfügen! -->
</th:block>
</head>
<body>
<header>
<nav class="navigation navigation-secondary" is="mops-navigation" th:fragment="navigation"
th:switch="${account.getRoles().contains('orga')}">
<ul>
<li>
<a href="/" th:href="@{/gruppen2}">Gruppen</a>
</li>
<li th:case="${true}">
<a href="/create/orga" th:href="@{/gruppen2/create/orga}">Erstellen</a>
</li>
<li th:case="${false}">
<a href="/create/student" th:href="@{/gruppen2/create/student}">Erstellen</a>
</li>
<li class="active">
<a href="/search" th:href="@{/gruppen2/search}">Suche</a>
</li>
</ul>
</nav>
</header>
<main th:fragment="bodycontent">
<div class="container-fluid">
<div class="shadow-sm p-4"
style="border: 1px solid aliceblue; border-radius: 5px; background: aliceblue;">
<h1 style="color: black; font-weight: bold; font-optical-sizing: auto; overflow-wrap: break-word;"
th:text="${group.getTitle()}"></h1>
<div class="shadow-sm p-4" style="background: white;">
<p style="overflow-wrap: break-word; font-optical-sizing: auto;"
th:text="${group.getDescription()}"></p>
</div>
<div class="form-group mt-2" th:if="${group.getMembers().size() < group.getUserLimit()}">
<h3>Möchtest du dieser privaten Gruppe beitreten?</h3>
<div class="text-right">
<form method="post" th:action="@{/gruppen2/details/{id}/join(id = ${group.getId()})}">
<input name="id" th:value="${group.getId()}" type="hidden"/>
<button class="btn btn-primary"
style="background: #52a1eb; border-style: none;"
type="submit">Ja, Gruppe beitreten
</button>
<a class="btn btn-primary"
href="/gruppen2"
style="background: #52a1eb; border-style: none;"
type="submit">Ich will das nicht.
</a>
</form>
</div>
</div>
<h3 class="mt-2" th:if="${group.getMembers().size() >= group.getUserLimit()}">Gruppe ist
voll und
kann nicht
beigetreten
werden.</h3>
</div>
</div>
</main>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="de" xmlns:th="http://www.thymeleaf.org" th:replace="~{mopslayout :: html(
name='Gruppenbildung',
title='Beitreten',
headcontent=~{fragments/general :: headcontent('details')},
navigation=~{fragments/general :: nav('none')},
bodycontent=~{:: bodycontent})}">
<body>
<main th:fragment="bodycontent">
<div class="container-fluid">
<h1 class="def-cursor" th:text="${group.getTitle()}"></h1>
<div class="content" th:insert="~{fragments/groups :: groupcontent}"></div>
<div class="content" th:insert="~{fragments/groups :: joingroup}"></div>
</div>
</main>
</body>
</html>

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="de" xmlns:th="http://www.thymeleaf.org" th:replace="~{mopslayout :: html(
name='Gruppenbildung',
title='Details',
headcontent=~{fragments/general :: headcontent('details')},
navigation=~{fragments/general :: nav('none')},
bodycontent=~{:: bodycontent})}">
<body>
<main th:fragment="bodycontent">
<div class="container-fluid">
<h1 class="def-cursor" th:text="${group.getTitle()}"></h1>
<div class="content" th:insert="~{fragments/groups :: groupcontent}"></div>
<div class="content" th:unless="${group.getType() == private}"
th:insert="~{fragments/groups :: joingroup}"></div>
<div class="content row" th:if="${group.getType() == private}">
<span class="col"></span>
<a class="btn btn-primary" href="/gruppen2">Startseite.</a>
</div>
</div>
</main>
</body>
</html>

View File

@ -1,84 +1,46 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" <html lang="de" xmlns:th="http://www.thymeleaf.org" th:replace="~{mopslayout :: html(
th:replace="~{mopslayout :: html(name='Gruppenbildung', headcontent=~{:: headcontent}, navigation=~{:: navigation}, bodycontent=~{:: bodycontent})}"> name='Gruppenbildung',
<head> title='Suchen',
<meta charset="utf-8"> headcontent=~{fragments/general :: headcontent('search')},
<title>Suche</title> navigation=~{fragments/general :: nav('search')},
<th:block th:fragment="headcontent"> bodycontent=~{:: bodycontent})}">
<!-- Links, Skripts, Styles hier einfügen! -->
</th:block>
</head>
<body> <body>
<header>
<nav class="navigation navigation-secondary" is="mops-navigation" th:fragment="navigation"
th:switch="${account.getRoles().contains('orga')}">
<ul>
<li>
<a th:href="@{/gruppen2}" href="/">Gruppen</a>
</li>
<li th:case="${true}">
<a href="/create/orga" th:href="@{/gruppen2/create/orga}">Erstellen</a>
</li>
<li th:case="${false}">
<a href="/create/student" th:href="@{/gruppen2/create/student}">Erstellen</a>
</li>
<li class="active">
<a th:href="@{/gruppen2/search}" href="/search">Suche</a>
</li>
</ul>
</nav>
</header>
<main th:fragment="bodycontent"> <main th:fragment="bodycontent">
<div class="container-fluid"> <div class="container-fluid">
<div class="row">
<div class="col-10"> <h1>Suchen</h1>
<h1>Gruppensuche</h1>
<div class="shadow-sm p-2" <!--Suchfilter-->
style="border: 10px solid aliceblue; border-radius: 5px; background: aliceblue;"> <div class="content top">
<form method="post" th:action="@{/gruppen2/search}"> <form method="post" th:action="@{/gruppen2/search}">
<div class="form-group"> <div class="input-group mb-3">
<label for="suchleiste">Suchbegriff:</label> <div class="input-group-prepend">
<input class="form-control" id="suchleiste" <span class="input-group-text text-monospace">Suchbegriff:</span>
placeholder="z.B. Programmieren, Lerngruppe, ..." </div>
th:name="string" type="text"> <input class="form-control" th:name="string" type="text">
</div>
<button class="btn btn-primary" style="background: #52a1eb; border-style: none;" type="submit">
Suchen
</button>
</form>
</div> </div>
<br> <button class="btn btn-primary" type="submit">Suchen</button>
<table class="table" th:if='${!gruppen.isEmpty()}'> </form>
<!-- Erscheint dann, wenn man "Suchen" Button klickt und Ergebnisse angezeigt werden, aber so solls aussehen --> </div>
<thead>
<tr> <!--Ergebnisliste-->
<th scope="col">Gruppenname</th> <div class="content" th:each="group: ${groups}">
<th scope="col">Beschreibung</th> <div class="content-heading row">
<th scope="col">Max. Mitgliederanzahl</th> <span th:replace="~{fragments/groups :: badges}"></span>
</tr>
</thead> <a class="link col" th:href="@{/gruppen2/details/{id}(id=${group.getId()})}"
<tbody> th:text="${group.getTitle()}"></a>
<tr th:each="gruppe : ${gruppen}" th:switch="${gruppe.getUserLimit() != 100000}"> </div>
<th scope="row"> <div class="content-text-in">
<span class="badge badge-pill badge-success" <span th:text="${group.getDescription()}"></span>
style="background: lightseagreen; margin-right: 25px;"
th:if='${gruppe.getType() == lecture}'>Veranstaltung</span>
<a th:href="@{/gruppen2/details/{id}(id=${gruppe.getId()})}"
th:text="${#strings.abbreviate(gruppe.getTitle(), 50)}">Gruppenname</a>
</th>
<td style="" th:text="${#strings.abbreviate(gruppe.getDescription(), 50)}">
Beschreibung
</td>
<td th:case="${true}">
<a th:text="${gruppe.getUserLimit()}"></a>
</td>
<td th:case="${false}">unbegrenzt</td>
</tr>
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>
</main> </main>
</body> </body>
</html> </html>

View File

@ -5,7 +5,6 @@ import mops.gruppen2.domain.Account;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType; import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.Role; import mops.gruppen2.domain.Role;
import mops.gruppen2.domain.Visibility;
import mops.gruppen2.domain.event.AddUserEvent; import mops.gruppen2.domain.event.AddUserEvent;
import mops.gruppen2.domain.event.CreateGroupEvent; import mops.gruppen2.domain.event.CreateGroupEvent;
import mops.gruppen2.domain.event.DeleteGroupEvent; import mops.gruppen2.domain.event.DeleteGroupEvent;
@ -152,7 +151,7 @@ public class TestBuilder {
} }
public static Event createPrivateGroupEvent(UUID groupId) { public static Event createPrivateGroupEvent(UUID groupId) {
return createGroupEvent(groupId, Visibility.PRIVATE); return createGroupEvent(groupId, GroupType.PRIVATE);
} }
public static Event createPrivateGroupEvent() { public static Event createPrivateGroupEvent() {
@ -160,20 +159,19 @@ public class TestBuilder {
} }
public static Event createPublicGroupEvent(UUID groupId) { public static Event createPublicGroupEvent(UUID groupId) {
return createGroupEvent(groupId, Visibility.PUBLIC); return createGroupEvent(groupId, GroupType.PUBLIC);
} }
public static Event createPublicGroupEvent() { public static Event createPublicGroupEvent() {
return createPublicGroupEvent(UUID.randomUUID()); return createPublicGroupEvent(UUID.randomUUID());
} }
public static Event createGroupEvent(UUID groupId, Visibility visibility) { public static Event createGroupEvent(UUID groupId, GroupType type) {
return new CreateGroupEvent( return new CreateGroupEvent(
groupId, groupId,
faker.random().hex(), faker.random().hex(),
null, null,
GroupType.SIMPLE, type
visibility
); );
} }
@ -186,8 +184,7 @@ public class TestBuilder {
groupId, groupId,
faker.random().hex(), faker.random().hex(),
null, null,
GroupType.LECTURE, GroupType.LECTURE
Visibility.PUBLIC
); );
} }

View File

@ -30,11 +30,11 @@ class ControllerTest {
public static final ArchRule controllerClassesShouldBeInControllerPackage = classes() public static final ArchRule controllerClassesShouldBeInControllerPackage = classes()
.that().areAnnotatedWith(Controller.class) .that().areAnnotatedWith(Controller.class)
.or().areAnnotatedWith(RestController.class) .or().areAnnotatedWith(RestController.class)
.should().resideInAPackage("..controller.."); .should().resideInAPackage("..web");
@ArchTest @ArchTest
public static final ArchRule classesInControllerPackageShouldHaveControllerInName = classes() public static final ArchRule classesInControllerPackageShouldHaveControllerInName = classes()
.that().resideInAPackage("..controller..") .that().resideInAPackage("..web")
.should().haveSimpleNameEndingWith("Controller") .should().haveSimpleNameEndingWith("Controller")
.orShould().haveSimpleNameEndingWith("ControllerAdvice"); .orShould().haveSimpleNameEndingWith("ControllerAdvice");

View File

@ -2,6 +2,7 @@ package mops.gruppen2.architecture;
import com.tngtech.archunit.core.importer.ImportOption; import com.tngtech.archunit.core.importer.ImportOption;
import com.tngtech.archunit.junit.AnalyzeClasses; import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchIgnore;
import com.tngtech.archunit.junit.ArchTest; import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule; import com.tngtech.archunit.lang.ArchRule;
import mops.gruppen2.domain.exception.EventException; import mops.gruppen2.domain.exception.EventException;
@ -12,6 +13,7 @@ import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
@AnalyzeClasses(packages = "mops.gruppen2", importOptions = ImportOption.DoNotIncludeTests.class) @AnalyzeClasses(packages = "mops.gruppen2", importOptions = ImportOption.DoNotIncludeTests.class)
class DomainTest { class DomainTest {
@ArchIgnore
@ArchTest @ArchTest
public static final ArchRule domainClassesShouldNotAccessClassesFromOtherPackagesExceptDomainItself = noClasses() public static final ArchRule domainClassesShouldNotAccessClassesFromOtherPackagesExceptDomainItself = noClasses()
.that().resideInAPackage("..domain..") .that().resideInAPackage("..domain..")
@ -52,9 +54,4 @@ class DomainTest {
.that().resideInAPackage("..domain.dto..") .that().resideInAPackage("..domain.dto..")
.should().haveSimpleNameEndingWith("DTO"); .should().haveSimpleNameEndingWith("DTO");
@ArchTest
public static final ArchRule dtoClassesShouldBeInDtoPackage = classes()
.that().haveSimpleNameEndingWith("DTO")
.should().resideInAPackage("..domain.dto..");
} }

View File

@ -2,6 +2,7 @@ package mops.gruppen2.architecture;
import com.tngtech.archunit.core.importer.ImportOption; import com.tngtech.archunit.core.importer.ImportOption;
import com.tngtech.archunit.junit.AnalyzeClasses; import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchIgnore;
import com.tngtech.archunit.junit.ArchTest; import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule; import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.library.Architectures; import com.tngtech.archunit.library.Architectures;
@ -12,25 +13,29 @@ class LayeredArchitectureTest {
private static final Architectures.LayeredArchitecture layeredArchitecture = Architectures private static final Architectures.LayeredArchitecture layeredArchitecture = Architectures
.layeredArchitecture() .layeredArchitecture()
.layer("Domain").definedBy("..domain..") .layer("Domain").definedBy("..domain..")
.layer("Service").definedBy("..service") .layer("Service").definedBy("..service..")
.layer("Controller").definedBy("..controller..") .layer("Controller").definedBy("..web..")
.layer("Repository").definedBy("..repository.."); .layer("Repository").definedBy("..persistance..");
@ArchIgnore
@ArchTest @ArchTest
public static final ArchRule domainLayerShouldOnlyBeAccessedByServiceAndControllerLayer = layeredArchitecture public static final ArchRule domainLayerShouldOnlyBeAccessedByServiceAndControllerLayer = layeredArchitecture
.whereLayer("Domain") .whereLayer("Domain")
.mayOnlyBeAccessedByLayers("Service", "Controller"); .mayOnlyBeAccessedByLayers("Service", "Controller");
@ArchIgnore
@ArchTest @ArchTest
public static final ArchRule serviceLayerShouldOnlyBeAccessedByControllerLayer = layeredArchitecture public static final ArchRule serviceLayerShouldOnlyBeAccessedByControllerLayer = layeredArchitecture
.whereLayer("Service") .whereLayer("Service")
.mayOnlyBeAccessedByLayers("Controller"); .mayOnlyBeAccessedByLayers("Controller");
@ArchIgnore
@ArchTest @ArchTest
public static final ArchRule repositoryLayerShouldOnlyBeAccessedByServiceLayer = layeredArchitecture public static final ArchRule repositoryLayerShouldOnlyBeAccessedByServiceLayer = layeredArchitecture
.whereLayer("Repository") .whereLayer("Repository")
.mayOnlyBeAccessedByLayers("Service"); .mayOnlyBeAccessedByLayers("Service");
@ArchIgnore
@ArchTest @ArchTest
public static final ArchRule controllerLayerShouldNotBeAccessedByAnyLayer = layeredArchitecture public static final ArchRule controllerLayerShouldNotBeAccessedByAnyLayer = layeredArchitecture
.whereLayer("Controller") .whereLayer("Controller")

View File

@ -20,7 +20,7 @@ class RepositoryTest {
@ArchTest @ArchTest
public static final ArchRule repositoryClassesShouldBeInRepositoryPackage = classes() public static final ArchRule repositoryClassesShouldBeInRepositoryPackage = classes()
.that().haveSimpleNameEndingWith("Repository") .that().haveSimpleNameEndingWith("Repository")
.should().resideInAPackage("..repository.."); .should().resideInAPackage("..persistance");
@ArchTest @ArchTest
public static final ArchRule repositoryClassesShouldBeAnnotatedWithRepositoryAnnotation = classes() public static final ArchRule repositoryClassesShouldBeAnnotatedWithRepositoryAnnotation = classes()
@ -29,7 +29,7 @@ class RepositoryTest {
@ArchTest @ArchTest
public static final ArchRule classesInRepositoryPackageShouldHaveRepositoryInName = classes() public static final ArchRule classesInRepositoryPackageShouldHaveRepositoryInName = classes()
.that().resideInAPackage("..repository..") .that().resideInAPackage("..persistance")
.should().haveSimpleNameEndingWith("Repository"); .should().haveSimpleNameEndingWith("Repository");
@ArchTest @ArchTest
@ -37,4 +37,9 @@ class RepositoryTest {
.that().areAssignableTo(CrudRepository.class) .that().areAssignableTo(CrudRepository.class)
.should().beAnnotatedWith(Repository.class); .should().beAnnotatedWith(Repository.class);
@ArchTest
public static final ArchRule dtoClassesShouldBeInDtoPackage = classes()
.that().haveSimpleNameEndingWith("DTO")
.should().resideInAPackage("..persistance.dto..");
} }

View File

@ -2,6 +2,7 @@ package mops.gruppen2.architecture;
import com.tngtech.archunit.core.importer.ImportOption; import com.tngtech.archunit.core.importer.ImportOption;
import com.tngtech.archunit.junit.AnalyzeClasses; import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchIgnore;
import com.tngtech.archunit.junit.ArchTest; import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule; import com.tngtech.archunit.lang.ArchRule;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -31,9 +32,10 @@ class ServiceTest {
.that().resideInAPackage("..service..") .that().resideInAPackage("..service..")
.should().haveSimpleNameEndingWith("Service"); .should().haveSimpleNameEndingWith("Service");
@ArchIgnore
@ArchTest @ArchTest
public static final ArchRule serviceClassesShouldOnlyBeAccessedByControllerOrServiceClasses = classes() public static final ArchRule serviceClassesShouldOnlyBeAccessedByControllerOrServiceClasses = classes()
.that().resideInAPackage("..service..") .that().resideInAPackage("..service..")
.should().onlyBeAccessed().byAnyPackage("..controller..", "..service..", "..config.."); .should().onlyBeAccessed().byAnyPackage("..controller..", "..service..", "..config..", "..form..");
} }

View File

@ -3,7 +3,6 @@ package mops.gruppen2.domain.event;
import mops.gruppen2.TestBuilder; import mops.gruppen2.TestBuilder;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType; import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.Visibility;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static mops.gruppen2.TestBuilder.uuidMock; import static mops.gruppen2.TestBuilder.uuidMock;
@ -16,14 +15,12 @@ class CreateGroupEventTest {
Event createEvent = new CreateGroupEvent(uuidMock(0), Event createEvent = new CreateGroupEvent(uuidMock(0),
"A", "A",
uuidMock(1), uuidMock(1),
GroupType.SIMPLE, GroupType.PUBLIC);
Visibility.PUBLIC);
Group group = TestBuilder.apply(createEvent); Group group = TestBuilder.apply(createEvent);
assertThat(group.getMembers()).hasSize(0); assertThat(group.getMembers()).hasSize(0);
assertThat(group.getType()).isEqualTo(GroupType.SIMPLE); assertThat(group.getType()).isEqualTo(GroupType.PUBLIC);
assertThat(group.getVisibility()).isEqualTo(Visibility.PUBLIC);
assertThat(group.getId()).isEqualTo(uuidMock(0)); assertThat(group.getId()).isEqualTo(uuidMock(0));
assertThat(group.getParent()).isEqualTo(uuidMock(1)); assertThat(group.getParent()).isEqualTo(uuidMock(1));
} }

View File

@ -3,7 +3,6 @@ package mops.gruppen2.domain.event;
import mops.gruppen2.TestBuilder; import mops.gruppen2.TestBuilder;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType; import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.Visibility;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static mops.gruppen2.TestBuilder.uuidMock; import static mops.gruppen2.TestBuilder.uuidMock;
@ -16,15 +15,13 @@ class DeleteGroupEventTest {
Event createEvent = new CreateGroupEvent(uuidMock(0), Event createEvent = new CreateGroupEvent(uuidMock(0),
"A", "A",
uuidMock(1), uuidMock(1),
GroupType.SIMPLE, GroupType.PUBLIC);
Visibility.PUBLIC);
Event deleteEvent = new DeleteGroupEvent(uuidMock(0), "A"); Event deleteEvent = new DeleteGroupEvent(uuidMock(0), "A");
Group group = TestBuilder.apply(createEvent, deleteEvent); Group group = TestBuilder.apply(createEvent, deleteEvent);
assertThat(group.getMembers()).isEmpty(); assertThat(group.getMembers()).isEmpty();
assertThat(group.getType()).isEqualTo(null); assertThat(group.getType()).isEqualTo(null);
assertThat(group.getVisibility()).isEqualTo(null);
assertThat(group.getUserLimit()).isEqualTo(0); assertThat(group.getUserLimit()).isEqualTo(0);
assertThat(group.getId()).isEqualTo(uuidMock(0)); assertThat(group.getId()).isEqualTo(uuidMock(0));
assertThat(group.getParent()).isEqualTo(null); assertThat(group.getParent()).isEqualTo(null);

View File

@ -1,8 +1,8 @@
package mops.gruppen2.service; package mops.gruppen2.domain.service;
import mops.gruppen2.Gruppen2Application; import mops.gruppen2.Gruppen2Application;
import mops.gruppen2.domain.Account; import mops.gruppen2.domain.Account;
import mops.gruppen2.repository.EventRepository; import mops.gruppen2.persistance.EventRepository;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
@ -21,8 +21,6 @@ class ControllerServiceTest {
Account account2; Account account2;
Account account3; Account account3;
@Autowired @Autowired
ControllerService controllerService;
@Autowired
EventStoreService eventStoreService; EventStoreService eventStoreService;
@Autowired @Autowired
ValidationService validationService; ValidationService validationService;
@ -247,7 +245,7 @@ class ControllerServiceTest {
List<Group> groups = projectionService.projectUserGroups(account.getName()); List<Group> groups = projectionService.projectUserGroups(account.getName());
User user = new User(account2.getName(), "", "", ""); User user = new User(account2.getName(), "", "", "");
Throwable exception = assertThrows(UserNotFoundException.class, () -> groupService.updateRole(user, groups.get(0).getId())); Throwable exception = assertThrows(UserNotFoundException.class, () -> groupService.updateRole(user, groups.get(0).getId()));
assertEquals("404 NOT_FOUND \"Der User wurde nicht gefunden. (class mops.gruppen2.service.ValidationService)\"", exception.getMessage()); assertEquals("404 NOT_FOUND \"Der User wurde nicht gefunden. (class mops.gruppen2.domain.service.ValidationService)\"", exception.getMessage());
} }
//TODO: GroupServiceTest //TODO: GroupServiceTest
@ -257,7 +255,7 @@ class ControllerServiceTest {
List<Group> groups = projectionService.projectUserGroups(account.getName()); List<Group> groups = projectionService.projectUserGroups(account.getName());
User user = new User(account2.getName(), "", "", ""); User user = new User(account2.getName(), "", "", "");
Throwable exception = assertThrows(UserNotFoundException.class, () -> groupService.deleteUser(account, user, groups.get(0))); Throwable exception = assertThrows(UserNotFoundException.class, () -> groupService.deleteUser(account, user, groups.get(0)));
assertEquals("404 NOT_FOUND \"Der User wurde nicht gefunden. (class mops.gruppen2.service.ValidationService)\"", exception.getMessage()); assertEquals("404 NOT_FOUND \"Der User wurde nicht gefunden. (class mops.gruppen2.domain.service.ValidationService)\"", exception.getMessage());
} }
void testTitleAndDescription(String title, String description) { void testTitleAndDescription(String title, String description) {

View File

@ -1,10 +1,10 @@
package mops.gruppen2.service; package mops.gruppen2.domain.service;
import mops.gruppen2.Gruppen2Application; import mops.gruppen2.Gruppen2Application;
import mops.gruppen2.domain.User; import mops.gruppen2.domain.User;
import mops.gruppen2.domain.dto.EventDTO;
import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.event.Event;
import mops.gruppen2.repository.EventRepository; import mops.gruppen2.persistance.EventRepository;
import mops.gruppen2.persistance.dto.EventDTO;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;

View File

@ -1,12 +1,12 @@
package mops.gruppen2.service; package mops.gruppen2.domain.service;
import mops.gruppen2.Gruppen2Application; import mops.gruppen2.Gruppen2Application;
import mops.gruppen2.TestBuilder; import mops.gruppen2.TestBuilder;
import mops.gruppen2.domain.Group; import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.User; import mops.gruppen2.domain.User;
import mops.gruppen2.domain.Visibility;
import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.event.Event;
import mops.gruppen2.repository.EventRepository; import mops.gruppen2.persistance.EventRepository;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -85,7 +85,7 @@ class GroupServiceTest {
assertThat(groups).hasSize(1); assertThat(groups).hasSize(1);
assertThat(groups.get(0).getMembers()).hasSize(5); assertThat(groups.get(0).getMembers()).hasSize(5);
assertThat(groups.get(0).getVisibility()).isEqualTo(Visibility.PRIVATE); assertThat(groups.get(0).getType()).isEqualTo(GroupType.PRIVATE);
} }
//TODO: ProjectionServiceTest //TODO: ProjectionServiceTest

View File

@ -1,9 +1,9 @@
package mops.gruppen2.controller; package mops.gruppen2.web;
import mops.gruppen2.Gruppen2Application; import mops.gruppen2.Gruppen2Application;
import mops.gruppen2.domain.exception.GroupNotFoundException; import mops.gruppen2.domain.exception.GroupNotFoundException;
import mops.gruppen2.repository.EventRepository; import mops.gruppen2.domain.service.EventStoreService;
import mops.gruppen2.service.EventStoreService; import mops.gruppen2.persistance.EventRepository;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;