diff --git a/build.gradle b/build.gradle index fa69883..2527211 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { - id 'org.springframework.boot' version '3.1.1' - id "io.spring.dependency-management" version "1.1.2" + id 'org.springframework.boot' version '3.1.5' + id "io.spring.dependency-management" version "1.1.3" id 'java' id 'war' id 'eclipse' @@ -8,7 +8,7 @@ plugins { } group = 'de.jottyfan.camporganizer' -version = '0.5.9' +version = '0.6.0' description = """CampOrganizer2""" @@ -41,29 +41,29 @@ war { } dependencies { - implementation 'org.apache.logging.log4j:log4j-api:2.20.0' - implementation 'org.apache.logging.log4j:log4j-core:2.20.0' - implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.20.0' + implementation 'org.apache.logging.log4j:log4j-api:2.21.0' + implementation 'org.apache.logging.log4j:log4j-core:2.21.0' + implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.21.0' - implementation 'org.webjars:bootstrap:5.2.3' - implementation 'org.webjars:font-awesome:5.15.4' - implementation 'org.webjars:jquery:3.6.4' - implementation 'org.webjars:popper.js:2.9.3' - implementation 'org.webjars:datatables:1.13.2' + implementation 'org.webjars:bootstrap:5.3.2' + implementation 'org.webjars:font-awesome:6.4.2' + implementation 'org.webjars:jquery:3.7.1' + implementation 'org.webjars:popper.js:2.11.7' + implementation 'org.webjars:datatables:1.13.5' 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 - implementation 'org.keycloak:keycloak-server-spi:22.0.1' - implementation 'org.keycloak:keycloak-admin-client:22.0.0' - implementation 'org.jboss.resteasy:resteasy-client:6.2.4.Final' + implementation 'org.keycloak:keycloak-server-spi:22.0.4' + implementation 'org.keycloak:keycloak-admin-client:22.0.4' + implementation 'org.jboss.resteasy:resteasy-client:6.2.5.Final' // backward compatibility until the complete registration is converted to keycloak implementation 'org.jasypt:jasypt:1.9.3' // rss support - implementation 'com.rometools:rome:1.18.0' + implementation 'com.rometools:rome:2.1.0' // mail support 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-security' 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-web' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6' 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' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/ConfirmationBoardController.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/ConfirmationBoardController.java new file mode 100644 index 0000000..1125905 --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/ConfirmationBoardController.java @@ -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"; + } +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/ConfirmationBoardRepository.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/ConfirmationBoardRepository.java new file mode 100644 index 0000000..35d8a71 --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/ConfirmationBoardRepository.java @@ -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 getCamps(String username) { + SelectSeekStep1, 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 list = new ArrayList<>(); + for (Record6 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 getPersons(String username, Integer campId) { + SelectConditionStep 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 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; + } +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/ConfirmationBoardService.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/ConfirmationBoardService.java new file mode 100644 index 0000000..c63dbef --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/ConfirmationBoardService.java @@ -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 loadCampList(String username) { + return repository.getCamps(username); + } + + public List loadPersonList(String username, Integer campId) { + return repository.getPersons(username, campId); + } +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/model/CampBean.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/model/CampBean.java new file mode 100644 index 0000000..d7895fa --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/model/CampBean.java @@ -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; + } +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/model/PersonBean.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/model/PersonBean.java new file mode 100644 index 0000000..6074759 --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/board/model/PersonBean.java @@ -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; + } +} diff --git a/src/main/resources/templates/confirmation/board.html b/src/main/resources/templates/confirmation/board.html new file mode 100644 index 0000000..46f5e2b --- /dev/null +++ b/src/main/resources/templates/confirmation/board.html @@ -0,0 +1,24 @@ + + + +Camp Organizer Confirmation + + + + +
+
+
+
+
+
+ in
+ anzeigen +
+
+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/confirmation/camplist.html b/src/main/resources/templates/confirmation/camplist.html new file mode 100644 index 0000000..07967af --- /dev/null +++ b/src/main/resources/templates/confirmation/camplist.html @@ -0,0 +1,59 @@ + + + +Camp Organizer Confirmation + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BezahltVornameNachnameAdresseKontaktGruppeGeburtsdatumStatusangemeldetFotoKommentar
+   +
+
+
+
+
+
+ +
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/template.html b/src/main/resources/templates/template.html index 1dd41aa..55aa8cc 100644 --- a/src/main/resources/templates/template.html +++ b/src/main/resources/templates/template.html @@ -4,15 +4,15 @@ Camp Organizer 2 - - - + + + - - - + + + @@ -89,6 +89,7 @@