diff --git a/src/main/java/de/hs_mannheim/informatik/ct/controller/RoomController.java b/src/main/java/de/hs_mannheim/informatik/ct/controller/RoomController.java
index 31f2863f..2fc89da8 100644
--- a/src/main/java/de/hs_mannheim/informatik/ct/controller/RoomController.java
+++ b/src/main/java/de/hs_mannheim/informatik/ct/controller/RoomController.java
@@ -46,6 +46,7 @@
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;
+import de.hs_mannheim.informatik.ct.util.CookieManager;
import de.hs_mannheim.informatik.ct.controller.exception.InvalidRoomPinException;
import de.hs_mannheim.informatik.ct.model.Room;
import de.hs_mannheim.informatik.ct.model.RoomVisit;
@@ -136,7 +137,7 @@ public String checkIn(@PathVariable String roomId,
@PostMapping("/checkIn")
@Transactional
- public String checkIn(@ModelAttribute RoomVisit.Data visitData, Model model) throws UnsupportedEncodingException, InvalidRoomPinException, InvalidEmailException, InvalidExternalUserdataException {
+ public String checkIn(@ModelAttribute RoomVisit.Data visitData, Model model, CookieManager cookieManager) throws UnsupportedEncodingException, InvalidRoomPinException, InvalidEmailException, InvalidExternalUserdataException {
isRoomPinValidOrThrow(visitData);
val room = roomService.getRoomOrThrow(visitData.getRoomId());
@@ -167,6 +168,8 @@ public String checkIn(@ModelAttribute RoomVisit.Data visitData, Model model) thr
// room manager should always be allowed to check-in
// and needs to be checked in before the browser is forwarded to a different page!
val visit = roomVisitService.visitRoom(visitor, room);
+
+ cookieManager.addCookie(CookieManager.Cookies.CHECKED_IN_EMAIL, visitorEmail);
if (visitData.isPrivileged()) {
val encodedVisitorEmail = URLEncoder.encode(visitorEmail, "UTF-8");
@@ -201,7 +204,7 @@ private void isRoomPinValidOrThrow(Data visitData) throws InvalidRoomPinExceptio
*/
@PostMapping("/checkInOverride")
@Transactional
- public String checkInWithOverride(@ModelAttribute RoomVisit.Data visitData, Model model) throws
+ public String checkInWithOverride(@ModelAttribute RoomVisit.Data visitData, Model model, CookieManager cookieManager) throws
UnsupportedEncodingException, InvalidEmailException, InvalidExternalUserdataException, InvalidRoomPinException {
// TODO: this method is very similar with the normal check-in, maybe this should be refactored?
@@ -219,6 +222,9 @@ public String checkInWithOverride(@ModelAttribute RoomVisit.Data visitData, Mode
roomVisitService.checkOutVisitor(visitor);
val visit = roomVisitService.visitRoom(visitor, room);
+
+ cookieManager.addCookie(CookieManager.Cookies.CHECKED_IN_EMAIL, visitorEmail);
+
val currentVisitCount = roomVisitService.getVisitorCount(room);
visitData = new RoomVisit.Data(visit, currentVisitCount);
model.addAttribute("visitData", visitData);
@@ -227,9 +233,9 @@ public String checkInWithOverride(@ModelAttribute RoomVisit.Data visitData, Mode
}
@PostMapping("/checkOut")
- public String checkOut(@ModelAttribute RoomVisit.Data visitData) {
+ public String checkOut(@ModelAttribute RoomVisit.Data visitData, CookieManager cookieManager) {
val visitor = getVisitorOrThrow(visitData.getVisitorEmail());
-
+ cookieManager.removeCookie(CookieManager.Cookies.CHECKED_IN_EMAIL);
roomVisitService.checkOutVisitor(visitor);
return "redirect:/r/checkedOut";
}
diff --git a/src/main/java/de/hs_mannheim/informatik/ct/controller/interceptor/CheckInInterceptor.java b/src/main/java/de/hs_mannheim/informatik/ct/controller/interceptor/CheckInInterceptor.java
new file mode 100644
index 00000000..c53e9d8e
--- /dev/null
+++ b/src/main/java/de/hs_mannheim/informatik/ct/controller/interceptor/CheckInInterceptor.java
@@ -0,0 +1,89 @@
+/*
+ * Corona Tracking Tool der Hochschule Mannheim
+ * Copyright (C) 2021 Hochschule Mannheim
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package de.hs_mannheim.informatik.ct.controller.interceptor;
+
+import de.hs_mannheim.informatik.ct.controller.Utilities;
+import de.hs_mannheim.informatik.ct.model.RoomVisit;
+import de.hs_mannheim.informatik.ct.persistence.services.RoomVisitService;
+import de.hs_mannheim.informatik.ct.persistence.services.VisitorService;
+import de.hs_mannheim.informatik.ct.util.CookieManager;
+import lombok.val;
+import lombok.var;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.lang.Nullable;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.List;
+
+public class CheckInInterceptor implements HandlerInterceptor {
+
+ @Autowired
+ private VisitorService visitorService;
+
+ @Autowired
+ private RoomVisitService roomVisitService;
+
+ @Autowired
+ private Utilities util;
+
+ private static final String CHECKED_IN_COOKIE_NAME = "checkedInEmail";
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
+ return true;
+ }
+
+ @Override
+ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+ @Nullable ModelAndView modelAndView) throws Exception {
+ val cookieManager = new CookieManager(request, response);
+ var isCheckedIn = false;
+ val checkedInEmail = cookieManager.getCookieValue(CookieManager.Cookies.CHECKED_IN_EMAIL);
+ if(checkedInEmail != null){
+ val checkedInRoom = getCheckedInRoomName(checkedInEmail);
+ if(checkedInRoom != null){
+ isCheckedIn = true;
+ request.setAttribute("checkedInRoom", checkedInRoom);
+ }else{
+ cookieManager.removeCookie(CookieManager.Cookies.CHECKED_IN_EMAIL);
+ }
+ }
+ request.setAttribute("checkedInEmail", checkedInEmail);
+ request.setAttribute("isCheckedIn", isCheckedIn);
+ }
+
+ private String getCheckedInRoomName(String email){
+ List roomVisits = findCurrentRoomVisitsByEmail(email);
+ return roomVisits.size() > 0 ? roomVisits.get(0).getRoom().getName() : null;
+ }
+
+ private List findCurrentRoomVisitsByEmail(String email){
+ List roomVisits = new ArrayList<>();
+ val visitor = visitorService.findVisitorByEmail(email);
+ if(visitor.isPresent()) {
+ for(val roomVisit : roomVisitService.getCheckedInRoomVisits(visitor.get())){
+ roomVisits.add(roomVisit);
+ }
+ }
+ return roomVisits;
+ }
+}
diff --git a/src/main/java/de/hs_mannheim/informatik/ct/controller/resolver/CookieManagerResolver.java b/src/main/java/de/hs_mannheim/informatik/ct/controller/resolver/CookieManagerResolver.java
new file mode 100644
index 00000000..df72095f
--- /dev/null
+++ b/src/main/java/de/hs_mannheim/informatik/ct/controller/resolver/CookieManagerResolver.java
@@ -0,0 +1,50 @@
+/*
+ * Corona Tracking Tool der Hochschule Mannheim
+ * Copyright (C) 2021 Hochschule Mannheim
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package de.hs_mannheim.informatik.ct.controller.resolver;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.core.MethodParameter;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+import de.hs_mannheim.informatik.ct.util.CookieManager;
+
+public class CookieManagerResolver implements HandlerMethodArgumentResolver {
+
+ @Override
+ public boolean supportsParameter(MethodParameter methodParameter) {
+ return methodParameter.getParameter().getType() == CookieManager.class;
+ }
+
+ @Override
+ public Object resolveArgument(
+ MethodParameter methodParameter,
+ ModelAndViewContainer modelAndViewContainer,
+ NativeWebRequest nativeWebRequest,
+ WebDataBinderFactory webDataBinderFactory) throws Exception {
+
+ HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest();
+ HttpServletResponse response = (HttpServletResponse) nativeWebRequest.getNativeResponse();
+
+ return new CookieManager(request, response);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/de/hs_mannheim/informatik/ct/persistence/services/ContactTracingService.java b/src/main/java/de/hs_mannheim/informatik/ct/persistence/services/ContactTracingService.java
index e2bb5d96..e4acd080 100644
--- a/src/main/java/de/hs_mannheim/informatik/ct/persistence/services/ContactTracingService.java
+++ b/src/main/java/de/hs_mannheim/informatik/ct/persistence/services/ContactTracingService.java
@@ -37,7 +37,8 @@ public class ContactTracingService {
@NonNull
public List> getVisitorContacts(@NonNull Visitor visitor) {
- val contacts = new ArrayList>();
+ val contacts = new ArrayList>();
+
for (val service : visitServices) {
contacts.addAll(service.getVisitorContacts(visitor));
}
diff --git a/src/main/java/de/hs_mannheim/informatik/ct/util/CookieManager.java b/src/main/java/de/hs_mannheim/informatik/ct/util/CookieManager.java
new file mode 100644
index 00000000..e61472e8
--- /dev/null
+++ b/src/main/java/de/hs_mannheim/informatik/ct/util/CookieManager.java
@@ -0,0 +1,151 @@
+/*
+ * Corona Tracking Tool der Hochschule Mannheim
+ * Copyright (C) 2021 Hochschule Mannheim
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.hs_mannheim.informatik.ct.util;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.time.LocalTime;
+
+import org.springframework.web.util.WebUtils;
+
+import lombok.val;
+
+public class CookieManager {
+ private HttpServletRequest request;
+ private HttpServletResponse response;
+
+ public CookieManager(HttpServletRequest request){
+ this.request = request;
+ }
+
+ public CookieManager(HttpServletRequest request, HttpServletResponse response){
+ this(request);
+ this.response = response;
+ }
+
+ public enum Cookies{
+ CHECKED_IN_EMAIL("checkedInEmail");
+
+ String name;
+
+ Cookies(String name) {
+ this.name = name;
+ }
+
+ String getName(){
+ return this.name;
+ }
+ }
+
+ /**
+ * create a new cookie with a cookie factory
+ *
+ * @param cookieType cookie type
+ * @param value value of the cookie
+ */
+ private Cookie createCookie(Cookies cookieType, String value) {
+ Cookie cookie = null;
+ switch (cookieType){
+ case CHECKED_IN_EMAIL:
+ cookie = new CookieBuilder(cookieType.getName(), value)
+ .maxAge(getSecondsTill(LocalTime.parse(ScheduledMaintenanceTasks.FORCED_END_TIME)))
+ .build();
+ break;
+ }
+ return cookie;
+ }
+
+ /**
+ * add a cookie to the http response
+ *
+ * @param cookieType cookie type
+ * @param value value of the cookie
+ */
+ public void addCookie(Cookies cookieType, String value) {
+ val cookie = createCookie(cookieType, value);
+ response.addCookie(cookie);
+ }
+
+ /**
+ * remove a cookie from the http response
+ *
+ * @param cookieType cookie type
+ */
+ public void removeCookie(Cookies cookieType) {
+ Cookie cookie = new Cookie(cookieType.getName(), "");
+ cookie.setMaxAge(0);
+ cookie.setPath("/");
+ response.addCookie(cookie);
+ }
+
+ /**
+ * get a cookie from http request
+ *
+ * @param cookieType cookie type
+ */
+ public String getCookieValue(Cookies cookieType){
+ val cookie = WebUtils.getCookie(request, cookieType.getName());
+ return cookie!=null ? cookie.getValue() : null;
+ }
+
+ /**
+ * remove a cookie from the http response
+ *
+ * @param maxAgeEndTime time when the cookie should be invalid
+ * @return max age of the cookie
+ */
+ private int getSecondsTill(LocalTime maxAgeEndTime){
+ int now = LocalTime.now().toSecondOfDay();
+ int endTime = maxAgeEndTime.toSecondOfDay();
+ int nextDayEndTime = ((24 * 60 * 60) + endTime);
+ return (now > endTime) ? nextDayEndTime - now : endTime - now;
+ }
+
+ private static class CookieBuilder{
+ private String name, value, path;
+ private int maxAge;
+
+ public CookieBuilder(String name, String value){
+ this.name = name;
+ this.value = value;
+ this.path = "/";
+ this.maxAge = 0;
+ }
+
+ public CookieBuilder maxAge(int maxAge){
+ this.maxAge = maxAge;
+ return this;
+ }
+
+ public CookieBuilder path(String path){
+ this.path = path;
+ return this;
+ }
+
+ public Cookie build(){
+ val cookie = new Cookie(name, value);
+ cookie.setPath(path);
+ if(maxAge>0){
+ cookie.setMaxAge(maxAge);
+ }
+ return cookie;
+ }
+ }
+}
diff --git a/src/main/java/de/hs_mannheim/informatik/ct/util/ScheduledMaintenanceTasks.java b/src/main/java/de/hs_mannheim/informatik/ct/util/ScheduledMaintenanceTasks.java
index 5e63a05d..ef91b13a 100644
--- a/src/main/java/de/hs_mannheim/informatik/ct/util/ScheduledMaintenanceTasks.java
+++ b/src/main/java/de/hs_mannheim/informatik/ct/util/ScheduledMaintenanceTasks.java
@@ -43,7 +43,7 @@ public class ScheduledMaintenanceTasks {
private final int CRON_HOUR = 3;
private final int CRON_MINUTE = 55;
- private final String FORCED_END_TIME = "00:00:00";
+ public static final String FORCED_END_TIME = "00:00:00";
//@Scheduled(fixedRate = 5 * 60 * 1000) // Every 5 Minutes
@Scheduled(cron = "0 " + CRON_MINUTE + " " + CRON_HOUR + " * * *") // 3:55 AM
diff --git a/src/main/java/de/hs_mannheim/informatik/ct/web/WebConfig.java b/src/main/java/de/hs_mannheim/informatik/ct/web/WebConfig.java
new file mode 100644
index 00000000..e8400fa8
--- /dev/null
+++ b/src/main/java/de/hs_mannheim/informatik/ct/web/WebConfig.java
@@ -0,0 +1,51 @@
+/*
+ * Corona Tracking Tool der Hochschule Mannheim
+ * Copyright (C) 2021 Hochschule Mannheim
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.hs_mannheim.informatik.ct.web;
+
+import de.hs_mannheim.informatik.ct.controller.interceptor.CheckInInterceptor;
+import de.hs_mannheim.informatik.ct.controller.resolver.CookieManagerResolver;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.List;
+
+@Configuration
+public class WebConfig implements WebMvcConfigurer {
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(checkInInterceptor())
+ .addPathPatterns("/")
+ .addPathPatterns("/r/**");
+ }
+
+ @Override
+ public void addArgumentResolvers(
+ List resolvers) {
+ resolvers.add(new CookieManagerResolver());
+ }
+
+ @Bean
+ public CheckInInterceptor checkInInterceptor(){
+ return new CheckInInterceptor();
+ }
+}
diff --git a/src/main/resources/static/general.css b/src/main/resources/static/general.css
new file mode 100644
index 00000000..190af93e
--- /dev/null
+++ b/src/main/resources/static/general.css
@@ -0,0 +1,40 @@
+.danger-color {
+ background: #f8d7da;
+ border-color: #d9534f !important;
+}
+
+.custom-card {
+ display: flex;
+ flex-direction: column;
+ border: 2px solid #22376F;
+ border-radius: 3px;
+ margin: 5px;
+ padding: 5px;
+ background: #E9EBF1;
+}
+
+.custom-card .custom-card-footer {
+ display: flex;
+ align-items: center;
+}
+
+#check-in-alert-form{
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: wrap;
+}
+
+.hs-button {
+ border: 2px solid #22376F;
+ border-radius: 3px;
+ background: #22376F;
+ color: #FFF;
+ padding: 5px;
+ margin: 0;
+}
+
+.hs-button:hover {
+ background: #FFF;
+ color: #22376F;
+ cursor: pointer;
+}
diff --git a/src/main/resources/static/main.js b/src/main/resources/static/main.js
new file mode 100644
index 00000000..4c48c9c2
--- /dev/null
+++ b/src/main/resources/static/main.js
@@ -0,0 +1,7 @@
+const checkInAlertForm = document.getElementById('check-in-alert-form')
+
+checkInAlertForm.onsubmit = e => {
+ if(!confirm("Wollen Sie sich wirklich auschecken?")){
+ e.preventDefault();
+ }
+}
diff --git a/src/main/resources/templates/layout.html b/src/main/resources/templates/layout.html
index cb7fdec9..68a6cfb7 100644
--- a/src/main/resources/templates/layout.html
+++ b/src/main/resources/templates/layout.html
@@ -3,6 +3,7 @@
HSMA CTT
+
@@ -100,6 +101,7 @@
+
Hinweis: Die von dieser Seite verwendeten internen Cookies dienen nur zur
Verbesserung der Bedienbarkeit und werden nicht weitergegeben. Sie können Ihren Browser jederzeit so
@@ -112,6 +114,14 @@