diff --git a/build.gradle b/build.gradle index 6504dda..65e914d 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ apply plugin: 'war' apply plugin: 'application' group = 'de.jottyfan.camporganizer' -version = '0.4.1' +version = '0.4.2' sourceCompatibility = 17 mainClassName = "de.jottyfan.camporganizer.Main" diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/IPersonService.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/IPersonService.java deleted file mode 100644 index 190eec8..0000000 --- a/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/IPersonService.java +++ /dev/null @@ -1,48 +0,0 @@ -package de.jottyfan.camporganizer.module.confirmation.person; - -import java.util.List; - -import de.jottyfan.camporganizer.module.confirmation.person.impl.CampBean; -import de.jottyfan.camporganizer.module.confirmation.person.impl.PersonBean; - -/** - * - * @author jotty - * - */ -public interface IPersonService { - - /** - * get person bean from db if username can read it - * - * @param username the user to read from the database - * @param pk the ID of the person to be read - * @return the person or null - */ - public PersonBean getPerson(String username, Integer pk); - - /** - * update bean in the database - * - * @param bean the bean - * @param worker the user that is doing the changes - * @return number of affected database rows - */ - public Integer updatePerson(PersonBean bean, String worker); - - /** - * get all camps from the database that the user has access to - * - * @param username the user - * @return a list of camp beans; an empty one at least - */ - public List getCamps(String username); - - /** - * get several annotations as a String - * - * @param pk the ID of the person - * @return the annotations; may be an empty string - */ - public String getAnnotations(Integer pk); -} diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/PersonController.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/PersonController.java index 8b288fb..757bdd0 100644 --- a/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/PersonController.java +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/PersonController.java @@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.PostMapping; import de.jottyfan.camporganizer.module.camplist.CommonController; import de.jottyfan.camporganizer.module.confirmation.confirmation.IConfirmationService; import de.jottyfan.camporganizer.module.confirmation.person.impl.PersonBean; +import de.jottyfan.camporganizer.module.confirmation.person.impl.PersonService; /** * @@ -29,7 +30,7 @@ public class PersonController extends CommonController { private IConfirmationService confirmationService; @Autowired - private IPersonService personService; + private PersonService personService; @GetMapping("/confirmation/person/{pk}") public String getIndex(Model model, @PathVariable Integer pk) { diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonRepository.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonRepository.java index 12d9edd..d48fae9 100644 --- a/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonRepository.java +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonRepository.java @@ -34,6 +34,7 @@ import de.jottyfan.camporganizer.db.jooq.enums.EnumModule; import de.jottyfan.camporganizer.db.jooq.tables.TProfile; import de.jottyfan.camporganizer.db.jooq.tables.records.TCampRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord; +import de.jottyfan.camporganizer.db.jooq.tables.records.TProfileRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TRssRecord; import de.jottyfan.camporganizer.module.camplist.LambdaResultWrapper; import de.jottyfan.camporganizer.module.mail.MailBean; @@ -140,26 +141,30 @@ public class PersonRepository { LambdaResultWrapper lrw = new LambdaResultWrapper(); jooq.transaction(t -> { + SelectConditionStep sql0 = jooq.selectFrom(T_PROFILE).where(T_PROFILE.USERNAME.eq(registrator)); + LOGGER.debug(sql0.toString()); + Integer fkRegistrator = sql0.fetchOne(T_PROFILE.PK); + // get old accept value for comparison - SelectConditionStep sql = jooq.selectFrom(T_PERSON).where(T_PERSON.PK.eq(bean.getPk())); - LOGGER.debug(sql.toString()); - TPersonRecord r = sql.fetchOne(); + SelectConditionStep sql1 = jooq.selectFrom(T_PERSON).where(T_PERSON.PK.eq(bean.getPk())); + LOGGER.debug(sql1.toString()); + TPersonRecord r = sql1.fetchOne(); lrw.putBoolean("acceptOld", r == null ? null : r.getAccept()); lrw.putBoolean("acceptNew", bean.getAccept()); Integer fkCamp = r == null ? null : r.getFkCamp(); String email = r.getEmail(); // use the old one, too lrw.putString("oldEmail", email); - SelectConditionStep sql0 = jooq.selectFrom(T_CAMP).where(T_CAMP.PK.eq(fkCamp)); - LOGGER.debug(sql0.toString()); - TCampRecord rc = sql0.fetchOne(); + SelectConditionStep sql2 = jooq.selectFrom(T_CAMP).where(T_CAMP.PK.eq(fkCamp)); + LOGGER.debug(sql2.toString()); + TCampRecord rc = sql2.fetchOne(); String campName = rc == null ? null : rc.getName(); LocalDateTime arrive = rc == null ? null : rc.getArrive(); String campNameWithYear = new StringBuilder(campName == null ? "" : campName).append(" ") .append(arrive == null ? "" : arrive.format(DateTimeFormatter.ofPattern("YYYY"))).toString(); lrw.putString("campNameWithYear", campNameWithYear); - UpdateConditionStep sql1 = jooq + UpdateConditionStep sql3 = jooq // @formatter:off .update(T_PERSON) .set(T_PERSON.FORENAME, bean.getForename()) @@ -174,10 +179,11 @@ public class PersonRepository { .set(T_PERSON.COMMENT, bean.getComment()) .set(T_PERSON.ACCEPT, bean.getAccept()) .set(T_PERSON.CAMPROLE, bean.getCamprole()) + .set(T_PERSON.FK_REGISTRATOR, fkRegistrator) .where(T_PERSON.PK.eq(bean.getPk())); // @formatter:on - LOGGER.debug(sql1.toString()); - lrw.add(sql1.execute()); + LOGGER.debug(sql3.toString()); + lrw.add(sql3.execute()); // always StringBuilder buf = new StringBuilder("Eine Anmeldung für "); @@ -185,15 +191,15 @@ public class PersonRepository { buf.append(" wurde von ").append(registrator); buf.append(" korrigiert."); - InsertValuesStep2 sql2 = DSL.using(t) + InsertValuesStep2 sql4 = DSL.using(t) // @formatter:off .insertInto(T_RSS, T_RSS.MSG, T_RSS.RECIPIENT) .values(buf.toString(), "registrator"); // @formatter:on - LOGGER.debug("{}", sql2.toString()); - sql2.execute(); + LOGGER.debug("{}", sql4.toString()); + sql4.execute(); }); // send email to user instead of rss feed @@ -229,7 +235,7 @@ public class PersonRepository { buf.append(bean.getSurname()); buf.append(" zur Freizeit "); buf.append(campNameWithYear); - buf.append(" wurde bestätigt. Melde Dich jetzt unter https://www.onkelwernerfreizeiten.de/camporganizer an,"); + buf.append(" wurde bestätigt. Melde Dich jetzt unter https://www.onkelwernerfreizeiten.de an,"); buf.append(" um die zu unterschreibenden Dokumente herunterzuladen, auszudrucken und uns zukommen zu lassen."); sendMail = true; } @@ -300,7 +306,11 @@ public class PersonRepository { LocalDateTime created = r.get(T_PERSON.CREATED); EnumCamprole role = r.get(T_PERSON.CAMPROLE); String createdString = created.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")); - buf.append(String.format("angemeldet von %s %s am %s\n", bookerForename, bookerSurname, createdString)); + if (bookerForename == null && bookerSurname == null) { + buf.append(String.format("angemeldet ohne Registrierung am %s\n", createdString)); + } else { + buf.append(String.format("angemeldet von %s %s am %s\n", bookerForename, bookerSurname, createdString)); + } if (registratorForename != null || registratorSurname != null) { buf.append(String.format("bearbeitet von %s %s\n", registratorForename, registratorSurname)); } diff --git a/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonService.java b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonService.java index b47e99c..3e585a2 100644 --- a/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonService.java +++ b/src/main/java/de/jottyfan/camporganizer/module/confirmation/person/impl/PersonService.java @@ -5,36 +5,29 @@ import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import de.jottyfan.camporganizer.module.confirmation.person.IPersonService; - /** * * @author jotty * */ @Service -public class PersonService implements IPersonService { +public class PersonService { @Autowired private PersonRepository gateway; - @Override public PersonBean getPerson(String username, Integer pk) { return gateway.getPerson(username, pk); } - @Override public Integer updatePerson(PersonBean bean, String worker) { return gateway.updatePerson(bean, worker); } - @Override public List getCamps(String username) { return gateway.getCamps(username); } - @Override public String getAnnotations(Integer pk) { return gateway.getAnnotations(pk); } - } diff --git a/src/main/java/de/jottyfan/camporganizer/module/registration/RegistrationBean.java b/src/main/java/de/jottyfan/camporganizer/module/registration/RegistrationBean.java index 0181cc7..9cbc66f 100644 --- a/src/main/java/de/jottyfan/camporganizer/module/registration/RegistrationBean.java +++ b/src/main/java/de/jottyfan/camporganizer/module/registration/RegistrationBean.java @@ -11,6 +11,7 @@ import org.springframework.format.annotation.DateTimeFormat; import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole; import de.jottyfan.camporganizer.db.jooq.enums.EnumSex; +import de.jottyfan.camporganizer.module.registration.validate.TeacherAgeCheck; import de.jottyfan.camporganizer.module.registration.validate.UnusedUsername; /** @@ -19,6 +20,7 @@ import de.jottyfan.camporganizer.module.registration.validate.UnusedUsername; * */ @UnusedUsername(field = "login", message = "Dieses Login ist leider bereits vergeben. Bitte wähle ein anderes.") +@TeacherAgeCheck(field = "birthDate", fkCamp = "fkCamp", campRole = "campRole", message = "Als Mitarbeiter bist Du leider zu jung für diese Freizeit.") public class RegistrationBean implements Serializable { private static final long serialVersionUID = 1L; diff --git a/src/main/java/de/jottyfan/camporganizer/module/registration/RegistrationController.java b/src/main/java/de/jottyfan/camporganizer/module/registration/RegistrationController.java index be9bf80..743110e 100644 --- a/src/main/java/de/jottyfan/camporganizer/module/registration/RegistrationController.java +++ b/src/main/java/de/jottyfan/camporganizer/module/registration/RegistrationController.java @@ -52,7 +52,7 @@ public class RegistrationController extends CommonController { model.addAttribute("roles", EnumConverter.getRoles()); return "/registration/registration"; } - Boolean result = service.register(bean, (String) model.getAttribute("currentUser")); + Boolean result = service.register(bean, getCurrentUser()); return result ? "/registration/success" : "/error"; } diff --git a/src/main/java/de/jottyfan/camporganizer/module/registration/RegistrationRepository.java b/src/main/java/de/jottyfan/camporganizer/module/registration/RegistrationRepository.java index 11e3351..53a9e8c 100644 --- a/src/main/java/de/jottyfan/camporganizer/module/registration/RegistrationRepository.java +++ b/src/main/java/de/jottyfan/camporganizer/module/registration/RegistrationRepository.java @@ -112,31 +112,6 @@ public class RegistrationRepository { if (bean.getRegisterInKeycloak() && !loginNotYetInUse) { throw new DataAccessException("login already in use: " + bean.getLogin()); } - - // TODO: move to bean validator instead: - // check for valid birthdate of teachers - LocalDateTime birthDate = bean.getBirthDate().atStartOfDay(); - - if (EnumCamprole.teacher.equals(bean.getCampRole())) { - SelectConditionStep> sql = jooq - // @formatter:off - .select(T_CAMP.MAX_AGE, - DSL.localDateTimeDiff(T_CAMP.DEPART, birthDate).as("teacherAge")) - .from(T_CAMP) - .where(T_CAMP.PK.eq(bean.getFkCamp())); - // @formatter:on - LOGGER.debug(sql.toString()); - Record r = sql.fetchOne(); - Integer minTeacherAge = r.get(T_CAMP.MAX_AGE) + 2; // by default, we need 2 years older teachers at least - DayToSecond currentTeacherAge = r.get("teacherAge", DayToSecond.class); - double totalYears = currentTeacherAge.getTotalDays() / 365.25; // in years - int years = (int) totalYears; - if (years < minTeacherAge) { - throw new DataAccessException("Als Mitarbeiter bist Du leider zu jung für diese Freizeit."); - } - } - // end of check - Integer fkProfile = null; if (loginNotYetInUse) { String oldPassword = new StrongPasswordEncryptor().encryptPassword(bean.getPassword()); @@ -410,4 +385,34 @@ public class RegistrationRepository { LOGGER.debug("{}", sql.toString()); sql.execute(); } + + /** + * check if the age of the teacher in campRole fits to the valid ones + * + * @param birthDate the birth date of the person + * @param fkCamp the ID of the camp + * @param campRole the role; if it is a teacher, check for the age + * @return true if it fits, false otherwise + */ + public Boolean checkAgeOfTeacher(LocalDate birthDate, Integer fkCamp, EnumCamprole campRole) { + LocalDateTime birthDateDay = birthDate.atStartOfDay(); + if (EnumCamprole.teacher.equals(campRole)) { + SelectConditionStep> sql = jooq + // @formatter:off + .select(T_CAMP.MAX_AGE, + DSL.localDateTimeDiff(T_CAMP.DEPART, birthDateDay).as("teacherAge")) + .from(T_CAMP) + .where(T_CAMP.PK.eq(fkCamp)); + // @formatter:on + LOGGER.debug(sql.toString()); + Record r = sql.fetchOne(); + Integer minTeacherAge = r.get(T_CAMP.MAX_AGE) + 2; // by default, we need 2 years older teachers at least + DayToSecond currentTeacherAge = r.get("teacherAge", DayToSecond.class); + double totalYears = currentTeacherAge.getTotalDays() / 365.25; // in years + int years = (int) totalYears; + return years >= minTeacherAge; + } else { + return true; + } + } } diff --git a/src/main/java/de/jottyfan/camporganizer/module/registration/validate/TeacherAgeCheck.java b/src/main/java/de/jottyfan/camporganizer/module/registration/validate/TeacherAgeCheck.java new file mode 100644 index 0000000..91b4763 --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/registration/validate/TeacherAgeCheck.java @@ -0,0 +1,31 @@ +package de.jottyfan.camporganizer.module.registration.validate; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +/** + * + * @author jotty + * + */ +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Constraint(validatedBy = TeacherAgeCheckValidator.class) +@Documented +public @interface TeacherAgeCheck { + String message() default "teacher is too young"; + + String field(); + String fkCamp(); + String campRole(); + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/main/java/de/jottyfan/camporganizer/module/registration/validate/TeacherAgeCheckValidator.java b/src/main/java/de/jottyfan/camporganizer/module/registration/validate/TeacherAgeCheckValidator.java new file mode 100644 index 0000000..7c62788 --- /dev/null +++ b/src/main/java/de/jottyfan/camporganizer/module/registration/validate/TeacherAgeCheckValidator.java @@ -0,0 +1,58 @@ +package de.jottyfan.camporganizer.module.registration.validate; + +import java.text.DateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import org.springframework.beans.BeanWrapperImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.format.datetime.DateFormatter; + +import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole; +import de.jottyfan.camporganizer.module.registration.RegistrationRepository; + +/** + * + * @author jotty + * + */ +public class TeacherAgeCheckValidator implements ConstraintValidator { + + private String field; + private String fkCamp; + private String campRole; + private String message; + + @Autowired + private RegistrationRepository gateway; + + public void initialize(TeacherAgeCheck tac) { + this.field = tac.field(); + this.fkCamp = tac.fkCamp(); + this.campRole = tac.campRole(); + this.message = tac.message(); + } + + @Override + public boolean isValid(Object value, ConstraintValidatorContext context) { + Object birthDate = new BeanWrapperImpl(value).getPropertyValue(field); + Object campId = new BeanWrapperImpl(value).getPropertyValue(fkCamp); + Object roleInCamp = new BeanWrapperImpl(value).getPropertyValue(campRole); + Boolean result = true; + if (birthDate != null && campId != null) { + LocalDate bd = (LocalDate) birthDate; + Integer camp = (Integer) campId; + EnumCamprole role = (EnumCamprole) roleInCamp; + result = gateway.checkAgeOfTeacher(bd, camp, role); + if (!result) { + context.buildConstraintViolationWithTemplate(message).addPropertyNode(field).addConstraintViolation() + .disableDefaultConstraintViolation(); + } + } + return result; + } + +} diff --git a/src/main/resources/templates/confirmation/person.html b/src/main/resources/templates/confirmation/person.html index 17cf84e..8e4e8d9 100644 --- a/src/main/resources/templates/confirmation/person.html +++ b/src/main/resources/templates/confirmation/person.html @@ -17,13 +17,11 @@
-
+
-
-
-
+
@@ -35,25 +33,21 @@
-
+
-
-
-
+
- -
+ +
-
-
-
+
-
-
-
+
diff --git a/src/main/resources/templates/registration/registration.html b/src/main/resources/templates/registration/registration.html index ebcc20b..9628c05 100644 --- a/src/main/resources/templates/registration/registration.html +++ b/src/main/resources/templates/registration/registration.html @@ -35,7 +35,7 @@
[[${error}]]
- +