1

introduce createform for validation + userlimit set to actual + frontend changes

This commit is contained in:
Christoph
2020-04-12 13:14:33 +02:00
parent 2065f53209
commit 40470cc3a6
20 changed files with 199 additions and 175 deletions

View File

@ -1,26 +0,0 @@
package mops.gruppen2.domain.service;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.GroupType;
import org.springframework.stereotype.Service;
import java.util.UUID;
@Service
@Log4j2
public final class ControllerService {
private ControllerService() {}
public static GroupType getGroupType(String type) {
return GroupType.valueOf(type);
}
/**
* Ermittelt die UUID des Parents, falls vorhanden.
*/
public static UUID getParent(String parent, String type) {
return GroupType.valueOf(type) == GroupType.LECTURE ? IdService.emptyUUID() : IdService.stringToUUID(parent);
}
}

View File

@ -10,6 +10,7 @@ import mops.gruppen2.domain.exception.NoAccessException;
import mops.gruppen2.domain.exception.NoAdminAfterActionException;
import mops.gruppen2.domain.exception.UserAlreadyExistsException;
import mops.gruppen2.domain.exception.UserNotFoundException;
import mops.gruppen2.web.form.CreateForm;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.stereotype.Service;
@ -137,9 +138,9 @@ public final class ValidationService {
}
}
public static void validateGroupType(KeycloakAuthenticationToken token, String type) {
public static void validateCreateForm(KeycloakAuthenticationToken token, CreateForm form) {
if (!token.getAccount().getRoles().contains("orga")
&& GroupType.valueOf(type) == GroupType.LECTURE) {
&& form.getType() == GroupType.LECTURE) {
throw new BadParameterException("Eine Veranstaltung kann nur von ORGA erstellt werden.");
}
}

View File

@ -9,6 +9,7 @@ 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 org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Controller;
@ -16,16 +17,9 @@ 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 javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import static mops.gruppen2.domain.service.ControllerService.getGroupType;
import static mops.gruppen2.domain.service.ControllerService.getParent;
import javax.validation.Valid;
@SuppressWarnings("SameReturnValue")
@Log4j2
@ -55,26 +49,22 @@ public class GroupCreationController {
@PostMapping("/create")
@CacheEvict(value = "groups", allEntries = true)
public String postCreateOrga(KeycloakAuthenticationToken token,
@NotBlank @RequestParam("title") String title,
@NotBlank @RequestParam("description") String description,
@NotBlank @RequestParam("type") String type,
@Min(1) @Max(100_000) @RequestParam("userlimit") long userLimit,
@RequestParam(value = "parent", defaultValue = "") String parent,
@RequestParam(value = "file", required = false) MultipartFile file) {
@Valid CreateForm form) {
ValidationService.validateGroupType(token, type);
// Zusätzlicher check: studentin kann keine lecture erstellen
ValidationService.validateCreateForm(token, form);
User user = new User(token);
Group group = groupService.createGroup(user,
title,
description,
getGroupType(type),
userLimit,
getParent(parent, type));
form.getTitle(),
form.getDescription(),
form.getType(),
form.getUserlimit(),
form.getParent());
// ROLE_studentin kann kein CSV importieren
if (token.getAccount().getRoles().contains("orga")) {
groupService.addUsersToGroup(CsvService.readCsvFile(file), group, user);
groupService.addUsersToGroup(CsvService.readCsvFile(form.getFile()), group, user);
}
return "redirect:/gruppen2/details/" + IdService.uuidToString(group.getId());

View File

@ -64,10 +64,10 @@ public class GroupDetailsController {
// Detailseite für nicht-Mitglieder
if (!ValidationService.checkIfMember(group, user)) {
return "details_nomember";
return "preview";
}
return "details_member";
return "details";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@ -143,7 +143,7 @@ public class GroupDetailsController {
@CacheEvict(value = "groups", allEntries = true)
public String postDetailsMembersUpdateUserLimit(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId,
@Min(1) @Max(100_000) @RequestParam("userlimit") long userLimit) {
@Min(1) @Max(999_999) @RequestParam("userlimit") long userLimit) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));

View File

@ -0,0 +1,44 @@
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.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.util.UUID;
@Data
public class CreateForm {
@NotBlank
@Size(min = 3, max = 128)
String title;
@NotBlank
@Size(min = 3, max = 512)
String description;
@NotBlank
String type;
@NotBlank
String parent;
@Min(1)
@Max(999_999)
long userlimit;
MultipartFile file;
public GroupType getType() {
return GroupType.valueOf(type);
}
public UUID getParent() {
return getType() == GroupType.LECTURE ? IdService.emptyUUID() : IdService.stringToUUID(parent);
}
}

View File

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

View File

@ -2,8 +2,8 @@
<html lang="de" xmlns:th="http://www.thymeleaf.org" th:replace="~{mopslayout :: html(
name='Gruppenbildung',
title='Details',
headcontent=~{fragments/general :: headcontent(style='/details.css')},
navigation=~{fragments/general :: nav(current='none')},
headcontent=~{fragments/general :: headcontent('details')},
navigation=~{fragments/general :: nav('none')},
bodycontent=~{:: bodycontent})}">
<body>
@ -55,10 +55,7 @@
<!--Anzahl Text-->
<div class="mb-2">
<span>Teilnehmer: </span>
<span th:if="${group.getUserLimit() <= 100000}"
th:text="${group.getMembers().size() + ' von ' + group.getUserLimit()}"></span>
<span th:unless="${group.getUserLimit() <= 100000}"
th:text="${group.getMembers().size()}"></span>
<span th:text="${group.getMembers().size() + ' von ' + group.getUserLimit()}"></span>
</div>
<!--Bearbeiten-Button-->
@ -72,9 +69,10 @@
<!--Liste-->
<div class="members">
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between" th:each="member : ${group.getMembers()}">
<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 class="badge badge-success" th:if="${group.getRoles().get(member.getId()) == admin}">Admin</span>
<span th:replace="~{fragments/gruppen :: userbadges}"></span>
</li>
</ul>
</div>

View File

@ -2,8 +2,8 @@
<html lang="de" xmlns:th="http://www.thymeleaf.org" th:replace="~{mopslayout :: html(
name='Gruppenbildung',
title='Gruppe verwalten',
headcontent=~{fragments/general :: headcontent(style='none')},
navigation=~{fragments/general :: nav(current='none')},
headcontent=~{fragments/general :: headcontent('none')},
navigation=~{fragments/general :: nav('none')},
bodycontent=~{:: bodycontent})}">
<body>
@ -53,7 +53,7 @@
<form th:action="@{/gruppen2/details/{id}/edit/meta(id=${group.getId()})}"
method="post">
<div th:replace="~{fragments/gruppen :: meta}"></div>
<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>
@ -66,7 +66,7 @@
<form th:action="@{/gruppen2/details/{id}/edit/userlimit(id=${group.getId()})}"
method="post">
<div th:replace="~{fragments/gruppen :: userlimit}"></div>
<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>
@ -80,7 +80,7 @@
th:if="${account.getRoles().contains('orga')}"
enctype="multipart/form-data" method="post">
<div th:replace="~{fragments/gruppen :: csvimport}"></div>
<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>
@ -99,8 +99,7 @@
<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 class="badge badge-success align-self-start ml-2"
th:if="${group.getRoles().get(member.getId()) == admin}">Admin</span>
<span th:replace="~{fragments/gruppen :: userbadges}"></span>
</div>
<div class="d-flex">

View File

@ -3,8 +3,8 @@
<html lang="de" xmlns:th="http://www.thymeleaf.org" th:replace="~{mopslayout :: html(
name='Gruppenfindung',
title='Error',
headcontent=~{fragments/general :: headcontent(style='none')},
navigation=~{fragments/general :: nav(current='none')},
headcontent=~{fragments/general :: headcontent('none')},
navigation=~{fragments/general :: nav('none')},
bodycontent=~{:: bodycontent})}">
<body>

View File

@ -0,0 +1,106 @@
<!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">
<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>
</div>
<!--Gruppenbeschreibung-->
<div class="input-group">
<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></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">
<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">
<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')}">
<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

@ -9,10 +9,10 @@
<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="@{/script.js}"></script>
<script type="text/javascript" th:src="@{/js/script.js}"></script>
<link th:href="@{/style.css}" rel="stylesheet"/>
<link th:unless="${style == 'none'}" th:href="@{${style}}" rel="stylesheet"/>
<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-->

View File

@ -20,97 +20,9 @@
th:if='${group.getRoles().get(user.getId()) == admin}'>Admin</span>-->
</th:block>
<!--CSV-->
<div th:fragment="csvimport" class="input-group" th:if="${account.getRoles().contains('orga')}">
<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>
<!--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')">
<input type="radio" name="type" value="PRIVATE" checked> Privat
</label>
<label class="btn btn-secondary" onclick="enable('#parentselect')">
<input type="radio" name="type" value="PUBLIC"> Öffentlich
</label>
<label class="btn btn-secondary" onclick="disable('#parentselect')"
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">
<div class="input-group-prepend">
<span class="input-group-text text-monospace">Gehört zu:</span>
</div>
<select class="custom-select" id="parentselect" name="parent">
<option value="" 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="9223372036854775807">
<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" checked> Unbegrenzt
</label>
<label class="btn btn-secondary" onclick="enable('#limitselect'); disable('#limit')">
<input type="radio"> Begrenzt
</label>
</div>
<div class="input-group col-sm-8 pr-0">
<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()} : '1'"
min="1" max="100000" id="limitselect" required disabled>
<div class="input-group-append">
<span class="input-group-text text-monospace">Teilnehmer</span>
</div>
</div>
</div>
</th:block>
<th:block th:fragment="meta">
<!--Gruppentitel-->
<div class="input-group mb-2">
<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>
</div>
<!--Gruppenbeschreibung-->
<div class="input-group">
<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></textarea>
</div>
<!--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>

View File

@ -2,8 +2,8 @@
<html lang="de" xmlns:th="http://www.thymeleaf.org" th:replace="~{mopslayout :: html(
name='Gruppenfindung',
title='Meine Gruppen',
headcontent=~{fragments/general :: headcontent(style='/index.css')},
navigation=~{fragments/general :: nav(current='index')},
headcontent=~{fragments/general :: headcontent('index')},
navigation=~{fragments/general :: nav('index')},
bodycontent=~{:: bodycontent})}">
<body>

View File

@ -2,7 +2,7 @@
<html lang="de" xmlns:th="http://www.thymeleaf.org" th:replace="~{mopslayout :: html(
name='Gruppenbildung',
title='Details',
headcontent=~{fragments/general :: headcontent('/details.css')},
headcontent=~{fragments/general :: headcontent('details')},
navigation=~{fragments/general :: nav('none')},
bodycontent=~{:: bodycontent})}">

View File

@ -2,8 +2,8 @@
<html lang="de" xmlns:th="http://www.thymeleaf.org" th:replace="~{mopslayout :: html(
name='Gruppenbildung',
title='Suchen',
headcontent=~{fragments/general :: headcontent(style='/search.css')},
navigation=~{fragments/general :: nav(current='search')},
headcontent=~{fragments/general :: headcontent('search')},
navigation=~{fragments/general :: nav('search')},
bodycontent=~{:: bodycontent})}">
<body>