display camplists

This commit is contained in:
Jottyfan
2023-10-21 01:24:52 +02:00
parent 5f78b87a87
commit 3147007632
9 changed files with 631 additions and 25 deletions

View File

@ -1,6 +1,6 @@
plugins { plugins {
id 'org.springframework.boot' version '3.1.1' id 'org.springframework.boot' version '3.1.5'
id "io.spring.dependency-management" version "1.1.2" id "io.spring.dependency-management" version "1.1.3"
id 'java' id 'java'
id 'war' id 'war'
id 'eclipse' id 'eclipse'
@ -8,7 +8,7 @@ plugins {
} }
group = 'de.jottyfan.camporganizer' group = 'de.jottyfan.camporganizer'
version = '0.5.9' version = '0.6.0'
description = """CampOrganizer2""" description = """CampOrganizer2"""
@ -41,29 +41,29 @@ war {
} }
dependencies { dependencies {
implementation 'org.apache.logging.log4j:log4j-api:2.20.0' implementation 'org.apache.logging.log4j:log4j-api:2.21.0'
implementation 'org.apache.logging.log4j:log4j-core:2.20.0' implementation 'org.apache.logging.log4j:log4j-core:2.21.0'
implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.20.0' implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.21.0'
implementation 'org.webjars:bootstrap:5.2.3' implementation 'org.webjars:bootstrap:5.3.2'
implementation 'org.webjars:font-awesome:5.15.4' implementation 'org.webjars:font-awesome:6.4.2'
implementation 'org.webjars:jquery:3.6.4' implementation 'org.webjars:jquery:3.7.1'
implementation 'org.webjars:popper.js:2.9.3' implementation 'org.webjars:popper.js:2.11.7'
implementation 'org.webjars:datatables:1.13.2' implementation 'org.webjars:datatables:1.13.5'
implementation 'org.webjars:select2:4.0.13' implementation 'org.webjars:select2:4.0.13'
implementation 'net.sf.biweekly:biweekly:0.6.6' implementation 'net.sf.biweekly:biweekly:0.6.7'
// for using the keycloak rest interface // for using the keycloak rest interface
implementation 'org.keycloak:keycloak-server-spi:22.0.1' implementation 'org.keycloak:keycloak-server-spi:22.0.4'
implementation 'org.keycloak:keycloak-admin-client:22.0.0' implementation 'org.keycloak:keycloak-admin-client:22.0.4'
implementation 'org.jboss.resteasy:resteasy-client:6.2.4.Final' implementation 'org.jboss.resteasy:resteasy-client:6.2.5.Final'
// backward compatibility until the complete registration is converted to keycloak // backward compatibility until the complete registration is converted to keycloak
implementation 'org.jasypt:jasypt:1.9.3' implementation 'org.jasypt:jasypt:1.9.3'
// rss support // rss support
implementation 'com.rometools:rome:1.18.0' implementation 'com.rometools:rome:2.1.0'
// mail support // mail support
implementation 'commons-validator:commons-validator:1.7' implementation 'commons-validator:commons-validator:1.7'
@ -72,15 +72,15 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jooq' implementation 'org.springframework.boot:spring-boot-starter-jooq'
implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-security'
implementation "org.springframework.boot:spring-boot-starter-oauth2-client" implementation "org.springframework.boot:spring-boot-starter-oauth2-client"
implementation 'org.springframework.security:spring-security-oauth2-authorization-server:1.1.1' implementation 'org.springframework.security:spring-security-oauth2-authorization-server:1.1.3'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'de.jottyfan:COJooq:2023.03' implementation 'de.jottyfan:COJooq:2023.03'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.0.0' implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.3.0'
implementation 'commons-io:commons-io:2.13.0' implementation 'commons-io:commons-io:2.14.0'
runtimeOnly 'org.springframework.boot:spring-boot-starter-tomcat' runtimeOnly 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.boot:spring-boot-starter-test'

View File

@ -0,0 +1,35 @@
package de.jottyfan.camporganizer.module.confirmation.board;
import java.security.Principal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import de.jottyfan.camporganizer.module.camplist.CommonController;
/**
*
* @author jotty
*
*/
@Controller
public class ConfirmationBoardController extends CommonController {
@Autowired
private ConfirmationBoardService service;
@GetMapping("/confirmation/board")
public String getBoard(Model model, Principal principal) {
model.addAttribute("camps", service.loadCampList(super.getCurrentUser(principal)));
return "/confirmation/board";
}
@GetMapping("/confirmation/board/camp/{id}")
public String getCamplist(Model model, @PathVariable Integer id, Principal principal) {
model.addAttribute("persons", service.loadPersonList(super.getCurrentUser(principal), id));
return "/confirmation/camplist";
}
}

View File

@ -0,0 +1,107 @@
package de.jottyfan.camporganizer.module.confirmation.board;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPPROFILE;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSON;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PROFILE;
import static de.jottyfan.camporganizer.db.jooq.Tables.V_CAMP;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jooq.DSLContext;
import org.jooq.Record;
import org.jooq.Record6;
import org.jooq.SelectConditionStep;
import org.jooq.SelectSeekStep1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import de.jottyfan.camporganizer.module.confirmation.board.model.CampBean;
import de.jottyfan.camporganizer.module.confirmation.board.model.PersonBean;
import de.jottyfan.camporganizer.module.confirmation.confirmation.ConfirmationRepository;
/**
*
* @author jotty
*
*/
@Repository
public class ConfirmationBoardRepository {
private static final Logger LOGGER = LogManager.getLogger(ConfirmationRepository.class);
@Autowired
private DSLContext jooq;
/**
* get a list of camps that the user has access to
*
* @param username the username
* @return the list of camps; might be an empty list
*/
public List<CampBean> getCamps(String username) {
SelectSeekStep1<Record6<String, Double, Boolean, String, Integer, LocalDateTime>, LocalDateTime> sql = jooq
// @formatter:off
.select(V_CAMP.NAME, V_CAMP.YEAR, V_CAMP.IS_OVER, V_CAMP.LOCATION_NAME, V_CAMP.PK, V_CAMP.ARRIVE)
.from(T_PROFILE)
.leftJoin(T_CAMPPROFILE).on(T_CAMPPROFILE.FK_PROFILE.eq(T_PROFILE.PK))
.leftJoin(V_CAMP).on(V_CAMP.PK.eq(T_CAMPPROFILE.FK_CAMP))
.where(T_PROFILE.USERNAME.eq(username))
.orderBy(V_CAMP.ARRIVE);
// @formatter:on
LOGGER.trace(sql);
List<CampBean> list = new ArrayList<>();
for (Record6<String, Double, Boolean, String, Integer, LocalDateTime> r : sql.fetch()) {
CampBean bean = new CampBean();
bean.setPkCamp(r.get(V_CAMP.PK));
bean.setName(r.get(V_CAMP.NAME));
bean.setYear(r.get(V_CAMP.YEAR));
bean.setLocationName(r.get(V_CAMP.LOCATION_NAME));
bean.setIsOver(r.get(V_CAMP.IS_OVER));
list.add(bean);
}
return list;
}
/**
* get the list of persons for this camp; restrict by the user privilege
*
* @param username the user that needs access to this data
* @param campId the ID of the camp
* @return the list of persons; an empty list at least
*/
public List<PersonBean> getPersons(String username, Integer campId) {
SelectConditionStep<Record> sql = jooq
// @formatter:off
.select(T_PERSON.fields())
.from(T_PERSON)
.innerJoin(T_CAMPPROFILE).on(T_CAMPPROFILE.FK_CAMP.eq(T_PERSON.FK_CAMP))
.innerJoin(T_PROFILE).on(T_PROFILE.USERNAME.eq(username)).and(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE))
.where(T_PERSON.FK_CAMP.eq(campId));
// @formatter:on
LOGGER.trace(sql);
List<PersonBean> list = new ArrayList<>();
for (Record r : sql.fetch()) {
PersonBean bean = new PersonBean();
bean.setAccept(r.get(T_PERSON.ACCEPT));
bean.setBirthDate(r.get(T_PERSON.BIRTHDATE));
bean.setCamprole(r.get(T_PERSON.CAMPROLE) == null ? null : r.get(T_PERSON.CAMPROLE).getLiteral());
bean.setCity(r.get(T_PERSON.CITY));
bean.setComment(r.get(T_PERSON.COMMENT));
bean.setConsentCatalogPhoto(r.get(T_PERSON.CONSENT_CATALOG_PHOTO));
bean.setCreated(r.get(T_PERSON.CREATED));
bean.setEmail(r.get(T_PERSON.EMAIL));
bean.setForename(r.get(T_PERSON.FORENAME));
bean.setPaid(r.get(T_PERSON.PAID) == null ? null : r.get(T_PERSON.PAID).doubleValue());
bean.setPhone(r.get(T_PERSON.PHONE));
bean.setSex(r.get(T_PERSON.SEX) == null ? null : r.get(T_PERSON.SEX).getLiteral());
bean.setStreet(r.get(T_PERSON.STREET));
bean.setSurname(r.get(T_PERSON.SURNAME));
bean.setZip(r.get(T_PERSON.ZIP));
list.add(bean);
}
return list;
}
}

View File

@ -0,0 +1,29 @@
package de.jottyfan.camporganizer.module.confirmation.board;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.module.confirmation.board.model.CampBean;
import de.jottyfan.camporganizer.module.confirmation.board.model.PersonBean;
/**
*
* @author jotty
*
*/
@Service
public class ConfirmationBoardService {
@Autowired
private ConfirmationBoardRepository repository;
public List<CampBean> loadCampList(String username) {
return repository.getCamps(username);
}
public List<PersonBean> loadPersonList(String username, Integer campId) {
return repository.getPersons(username, campId);
}
}

View File

@ -0,0 +1,91 @@
package de.jottyfan.camporganizer.module.confirmation.board.model;
import java.io.Serializable;
/**
*
* @author jotty
*
*/
public class CampBean implements Serializable {
private static final long serialVersionUID = 1L;
private Integer pkCamp;
private String name;
private Double year;
private String locationName;
private Boolean isOver;
public String getTitle() {
return String.format("%s %4.0f", name, year);
}
/**
* @return the pkCamp
*/
public Integer getPkCamp() {
return pkCamp;
}
/**
* @param pkCamp the pkCamp to set
*/
public void setPkCamp(Integer pkCamp) {
this.pkCamp = pkCamp;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the year
*/
public Double getYear() {
return year;
}
/**
* @param year the year to set
*/
public void setYear(Double year) {
this.year = year;
}
/**
* @return the locationName
*/
public String getLocationName() {
return locationName;
}
/**
* @param locationName the locationName to set
*/
public void setLocationName(String locationName) {
this.locationName = locationName;
}
/**
* @return the isOver
*/
public Boolean getIsOver() {
return isOver;
}
/**
* @param isOver the isOver to set
*/
public void setIsOver(Boolean isOver) {
this.isOver = isOver;
}
}

View File

@ -0,0 +1,260 @@
package de.jottyfan.camporganizer.module.confirmation.board.model;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
*
* @author jotty
*
*/
public class PersonBean implements Serializable {
private static final long serialVersionUID = 1L;
private String forename;
private String surname;
private String street;
private String zip;
private String city;
private String phone;
private String email;
private LocalDate birthDate;
private String camprole;
private Boolean accept;
private LocalDateTime created;
private String sex;
private Double paid;
private String comment;
private Boolean consentCatalogPhoto;
public String getAge(LocalDate relation) {
return "?"; // TODO: calculate age in relation to the camp start.
}
public String getCamprolle() {
if ("student".equals(camprole)) {
return "Teilnehmer";
} else if ("teacher".equals(camprole)) {
return "Mitarbeiter";
} else if ("director".equals(camprole)) {
return "Leitung";
} else if ("feeder".equals(camprole)) {
return "Küche";
} else if ("observer".equals(camprole)) {
return "Mitarbeiterkind";
} else {
return null;
}
}
/**
* @return the forename
*/
public String getForename() {
return forename;
}
/**
* @param forename the forename to set
*/
public void setForename(String forename) {
this.forename = forename;
}
/**
* @return the surname
*/
public String getSurname() {
return surname;
}
/**
* @param surname the surname to set
*/
public void setSurname(String surname) {
this.surname = surname;
}
/**
* @return the street
*/
public String getStreet() {
return street;
}
/**
* @param street the street to set
*/
public void setStreet(String street) {
this.street = street;
}
/**
* @return the zip
*/
public String getZip() {
return zip;
}
/**
* @param zip the zip to set
*/
public void setZip(String zip) {
this.zip = zip;
}
/**
* @return the city
*/
public String getCity() {
return city;
}
/**
* @param city the city to set
*/
public void setCity(String city) {
this.city = city;
}
/**
* @return the phone
*/
public String getPhone() {
return phone;
}
/**
* @param phone the phone to set
*/
public void setPhone(String phone) {
this.phone = phone;
}
/**
* @return the email
*/
public String getEmail() {
return email;
}
/**
* @param email the email to set
*/
public void setEmail(String email) {
this.email = email;
}
/**
* @return the birthDate
*/
public LocalDate getBirthDate() {
return birthDate;
}
/**
* @param birthDate the birthDate to set
*/
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
/**
* @return the camprole
*/
public String getCamprole() {
return camprole;
}
/**
* @param camprole the camprole to set
*/
public void setCamprole(String camprole) {
this.camprole = camprole;
}
/**
* @return the accept
*/
public Boolean getAccept() {
return accept;
}
/**
* @param accept the accept to set
*/
public void setAccept(Boolean accept) {
this.accept = accept;
}
/**
* @return the created
*/
public LocalDateTime getCreated() {
return created;
}
/**
* @param created the created to set
*/
public void setCreated(LocalDateTime created) {
this.created = created;
}
/**
* @return the sex
*/
public String getSex() {
return sex;
}
/**
* @param sex the sex to set
*/
public void setSex(String sex) {
this.sex = sex;
}
/**
* @return the paid
*/
public Double getPaid() {
return paid;
}
/**
* @param paid the paid to set
*/
public void setPaid(Double paid) {
this.paid = paid;
}
/**
* @return the comment
*/
public String getComment() {
return comment;
}
/**
* @param comment the comment to set
*/
public void setComment(String comment) {
this.comment = comment;
}
/**
* @return the consentCatalogPhoto
*/
public Boolean getConsentCatalogPhoto() {
return consentCatalogPhoto;
}
/**
* @param consentCatalogPhoto the consentCatalogPhoto to set
*/
public void setConsentCatalogPhoto(Boolean consentCatalogPhoto) {
this.consentCatalogPhoto = consentCatalogPhoto;
}
}

View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" layout:decorate="~{template}" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<title>Camp Organizer Confirmation</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<th:block layout:fragment="content">
<div class="container" sec:authorize="hasRole('registrator')">
<div class="row g-2">
<div class="col-sm-4" th:each="c : ${camps}">
<div class="card">
<div class="card-header"><span th:text="${c.title}" th:style="${c.isOver ? 'color: #555' : 'font-weight: bolder'}"></span></div>
<div class="card-body">
in <span th:text="${c.locationName}"></span><br />
<a th:href="@{/confirmation/board/camp/{id}(id=${c.pkCamp})}" class="btn btn-outline-primary">anzeigen</a>
</div>
</div>
</div>
</div>
</div>
</th:block>
</body>
</html>

View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" layout:decorate="~{template}" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<title>Camp Organizer Confirmation</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<th:block layout:fragment="content">
<div sec:authorize="hasRole('registrator')" style="margin: 8px">
<table id="table" class="table table-striped">
<thead>
<tr>
<th>Bezahlt</th>
<th>Vorname</th>
<th>Nachname</th>
<th>Adresse</th>
<th>Kontakt</th>
<th>Gruppe</th>
<th>Geburtsdatum</th>
<th>Status</th>
<th>angemeldet</th>
<th>Foto</th>
<th>Kommentar</th>
</tr>
</thead>
<tbody>
<tr th:each="p : ${persons}">
<td th:text="${p.paid}"></td>
<td th:text="${p.forename}"></td>
<td th:text="${p.surname}"></td>
<td><div th:text="${p.street}"></div>
<span th:text="${p.zip}"></span>&nbsp;<span th:text="${p.city}"></span>
</td>
<td><div th:text="'Tel.: ' + ${p.phone}"></div>
<div th:text="${p.email}"></div>
</td>
<td><div th:text="${p.sex == 'male' ? 'männlich' : 'weiblich'}"></div>
<div th:text="${p.camprolle}"></div></td>
<td><div th:text="${#temporals.format(p.birthDate, 'dd.MM.yyyy')}"></div>
<div th:text="${p.getAge(p.birthDate)} + ' Jahre'"></div>
</td>
<td th:text="${p.accept}"></td>
<td th:text="${#temporals.format(p.created, 'dd.MM.yyyy HH:mm')}"></td>
<td><span th:text="${p.consentCatalogPhoto ? 'ja' : 'nein'}" th:if="${p.consentCatalogPhoto}"></span></td>
<td th:text="${p.comment}"></td>
</tr>
</tbody>
</table>
<script type="text/javascript">
$(document).ready(function() {
$("#table").DataTable({
language : locale_de
});
});
</script>
</div>
</th:block>
</body>
</html>

View File

@ -4,15 +4,15 @@
<title>Camp Organizer 2</title> <title>Camp Organizer 2</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/bootstrap/5.2.3/css/bootstrap.min.css} " /> <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/bootstrap/5.3.2/css/bootstrap.min.css} " />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/font-awesome/5.15.4/css/all.css} " /> <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/font-awesome/6.4.2/css/all.min.css} " />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/datatables/1.13.2/css/jquery.dataTables.min.css}" /> <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/datatables/1.13.5/css/jquery.dataTables.min.css}" />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/select2/4.0.13/css/select2.min.css}" /> <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/select2/4.0.13/css/select2.min.css}" />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/css/style.css}" /> <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/css/style.css}" />
<link th:rel="stylesheet" type="text/css" media="all" th:href="@{/css/select2-bootstrap-5-theme.min.css}" /> <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/css/select2-bootstrap-5-theme.min.css}" />
<script th:src="@{/webjars/jquery/3.6.4/jquery.min.js}"></script> <script th:src="@{/webjars/jquery/3.7.1/jquery.min.js}"></script>
<script th:src="@{/webjars/bootstrap/5.2.3/js/bootstrap.bundle.min.js}"></script> <script th:src="@{/webjars/bootstrap/5.3.2/js/bootstrap.bundle.min.js}"></script>
<script th:src="@{/webjars/datatables/1.13.2/js/jquery.dataTables.min.js}"></script> <script th:src="@{/webjars/datatables/1.13.5/js/jquery.dataTables.min.js}"></script>
<script th:src="@{/webjars/select2/4.0.13/js/select2.full.min.js}"></script> <script th:src="@{/webjars/select2/4.0.13/js/select2.full.min.js}"></script>
<script th:src="@{/js/dataTables.de.js}"></script> <script th:src="@{/js/dataTables.de.js}"></script>
<script th:src="@{/js/mytoggle.js}"></script> <script th:src="@{/js/mytoggle.js}"></script>
@ -89,6 +89,7 @@
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a th:href="@{/rss/registrator}" class="dropdown-item menufont"><i class="fas fa-rss"></i>&nbsp;RSS-Link</a></li> <li><a th:href="@{/rss/registrator}" class="dropdown-item menufont"><i class="fas fa-rss"></i>&nbsp;RSS-Link</a></li>
<li><a th:href="@{/confirmation}" class="dropdown-item menufont">Anmeldungsübersicht</a></li> <li><a th:href="@{/confirmation}" class="dropdown-item menufont">Anmeldungsübersicht</a></li>
<li><a th:href="@{/confirmation/board}" class="dropdown-item menufont">Listenübersicht</a></li>
</ul> </ul>
</div> </div>
</li> </li>