rss and mail
This commit is contained in:
		| @@ -16,13 +16,11 @@ import org.jooq.DSLContext; | ||||
| import org.jooq.Record; | ||||
| import org.jooq.SelectSeekStep1; | ||||
| import org.jooq.SelectSeekStep2; | ||||
| import org.jooq.UpdateConditionStep; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Repository; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
|  | ||||
| import de.jottyfan.camporganizer.db.jooq.tables.TProfile; | ||||
| import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord; | ||||
| import de.jottyfan.camporganizer.db.jooq.tables.records.VCampRecord; | ||||
|  | ||||
| /** | ||||
| @@ -132,28 +130,4 @@ public class IndexGateway { | ||||
| 		} | ||||
| 		return list; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * update defined fields of the bean | ||||
| 	 * | ||||
| 	 * @param bean the bean | ||||
| 	 * @return number of affected database rows; should be 1 | ||||
| 	 */ | ||||
| 	public Integer update(BookingBean bean) { | ||||
| 		UpdateConditionStep<TPersonRecord> sql = jooq | ||||
| 		// @formatter:off | ||||
| 			.update(T_PERSON) | ||||
| 			.set(T_PERSON.FORENAME, bean.getForename()) | ||||
| 			.set(T_PERSON.SURNAME, bean.getSurname()) | ||||
| 			.set(T_PERSON.STREET, bean.getStreet()) | ||||
| 			.set(T_PERSON.ZIP, bean.getZip()) | ||||
| 			.set(T_PERSON.CITY, bean.getCity()) | ||||
| 			.set(T_PERSON.PHONE, bean.getPhone()) | ||||
| 			.set(T_PERSON.EMAIL, bean.getEmail()) | ||||
| 			.set(T_PERSON.COMMENT, bean.getComment()) | ||||
| 			.where(T_PERSON.PK.eq(bean.getPk())); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug(sql.toString()); | ||||
| 		return sql.execute(); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -13,6 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| import de.jottyfan.camporganizer.db.jooq.tables.records.VCampRecord; | ||||
| import de.jottyfan.camporganizer.module.dashboard.DashboardGateway; | ||||
|  | ||||
| /** | ||||
|  * | ||||
| @@ -21,9 +22,13 @@ import de.jottyfan.camporganizer.db.jooq.tables.records.VCampRecord; | ||||
|  */ | ||||
| @Service | ||||
| public class IndexService { | ||||
|  | ||||
| 	@Autowired | ||||
| 	private IndexGateway gateway; | ||||
|  | ||||
| 	@Autowired | ||||
| 	private DashboardGateway dashboardGateway; | ||||
|  | ||||
| 	/** | ||||
| 	 * get all camps from the database and prepare them for the view | ||||
| 	 * | ||||
| @@ -54,6 +59,6 @@ public class IndexService { | ||||
| 	 * @return true or false | ||||
| 	 */ | ||||
| 	public Boolean update(BookingBean bean) { | ||||
| 		return gateway.update(bean) == 1; | ||||
| 		return dashboardGateway.update(bean) == 1; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,14 +1,21 @@ | ||||
| package de.jottyfan.camporganizer.module.common; | ||||
|  | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
|  | ||||
| /** | ||||
|  *  | ||||
|  * | ||||
|  * @author henkej | ||||
|  * | ||||
|  */ | ||||
| public class LambdaResultWrapper { | ||||
| 	private Integer counter; | ||||
| 	 | ||||
| 	private Map<String, Boolean> mapBoolean; | ||||
| 	private Map<String, String> mapString; | ||||
|  | ||||
| 	public LambdaResultWrapper() { | ||||
| 		this.mapBoolean = new HashMap<>(); | ||||
| 		this.mapString = new HashMap<>(); | ||||
| 		counter = 0; | ||||
| 	} | ||||
|  | ||||
| @@ -19,4 +26,20 @@ public class LambdaResultWrapper { | ||||
| 	public void add(Integer i) { | ||||
| 		counter += i; | ||||
| 	} | ||||
|  | ||||
| 	public void putBoolean(String key, Boolean value) { | ||||
| 		mapBoolean.put(key, value); | ||||
| 	} | ||||
|  | ||||
| 	public Boolean getBoolean(String key) { | ||||
| 		return mapBoolean.get(key); | ||||
| 	} | ||||
|  | ||||
| 	public void putString(String key, String value) { | ||||
| 		mapString.put(key, value); | ||||
| 	} | ||||
|  | ||||
| 	public String getString(String key) { | ||||
| 		return mapString.get(key); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -25,9 +25,10 @@ public interface IPersonService { | ||||
| 	 * 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); | ||||
| 	public Integer updatePerson(PersonBean bean, String worker); | ||||
|  | ||||
| 	/** | ||||
| 	 * get all camps from the database that the user has access to | ||||
|   | ||||
| @@ -42,7 +42,8 @@ public class PersonController { | ||||
|  | ||||
| 	@PostMapping("/confirmation/person/update") | ||||
| 	public String doUpdate(@ModelAttribute PersonBean bean, Model model) { | ||||
| 		personService.updatePerson(bean); | ||||
| 		String username = confirmationService.getCurrentUser(request); | ||||
| 		personService.updatePerson(bean, username); | ||||
| 		return "redirect:/confirmation"; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -5,23 +5,28 @@ import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPPROFILE; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_LOCATION; | ||||
| 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.T_RSS; | ||||
|  | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalDateTime; | ||||
| import java.time.format.DateTimeFormatter; | ||||
| import java.time.temporal.ChronoUnit; | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| import org.apache.logging.log4j.LogManager; | ||||
| import org.apache.logging.log4j.Logger; | ||||
| import org.jooq.DSLContext; | ||||
| import org.jooq.InsertValuesStep2; | ||||
| import org.jooq.Record; | ||||
| import org.jooq.Record11; | ||||
| import org.jooq.Record4; | ||||
| import org.jooq.SelectConditionStep; | ||||
| import org.jooq.SelectSeekStep1; | ||||
| import org.jooq.UpdateConditionStep; | ||||
| import org.jooq.impl.DSL; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Repository; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
| @@ -29,7 +34,11 @@ import org.springframework.transaction.annotation.Transactional; | ||||
| import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole; | ||||
| 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.TRssRecord; | ||||
| import de.jottyfan.camporganizer.module.common.LambdaResultWrapper; | ||||
| import de.jottyfan.camporganizer.module.mail.MailRepository; | ||||
|  | ||||
| /** | ||||
|  * | ||||
| @@ -44,6 +53,9 @@ public class PersonGateway { | ||||
| 	@Autowired | ||||
| 	private DSLContext jooq; | ||||
|  | ||||
| 	@Autowired | ||||
| 	private MailRepository mailRepository; | ||||
|  | ||||
| 	/** | ||||
| 	 * get all camps from the database if username is allowed to maintain it | ||||
| 	 * | ||||
| @@ -125,26 +137,119 @@ public class PersonGateway { | ||||
| 	 * @param bean the bean | ||||
| 	 * @return the number of affected database rows | ||||
| 	 */ | ||||
| 	public Integer updatePerson(PersonBean bean) { | ||||
| 		UpdateConditionStep<TPersonRecord> sql = jooq | ||||
| 		// @formatter:off | ||||
| 			.update(T_PERSON) | ||||
| 			.set(T_PERSON.FORENAME, bean.getForename()) | ||||
| 			.set(T_PERSON.SURNAME, bean.getSurname()) | ||||
| 			.set(T_PERSON.STREET, bean.getStreet()) | ||||
| 			.set(T_PERSON.ZIP, bean.getZip()) | ||||
| 			.set(T_PERSON.CITY, bean.getCity()) | ||||
| 			.set(T_PERSON.BIRTHDATE, bean.getBirthdate()) | ||||
| 			.set(T_PERSON.SEX, bean.getSex()) | ||||
| 			.set(T_PERSON.PHONE, bean.getPhone()) | ||||
| 			.set(T_PERSON.EMAIL, bean.getEmail()) | ||||
| 			.set(T_PERSON.COMMENT, bean.getComment()) | ||||
| 			.set(T_PERSON.ACCEPT, bean.getAccept()) | ||||
| 			.set(T_PERSON.CAMPROLE, bean.getCamprole()) | ||||
| 			.where(T_PERSON.PK.eq(bean.getPk())); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug(sql.toString()); | ||||
| 		return sql.execute(); | ||||
| 	public Integer updatePerson(PersonBean bean, String registrator) { | ||||
| 		LambdaResultWrapper lrw = new LambdaResultWrapper(); | ||||
| 		jooq.transaction(t -> { | ||||
|  | ||||
| 			// get old accept value for comparison | ||||
| 			SelectConditionStep<TPersonRecord> sql = jooq.selectFrom(T_PERSON).where(T_PERSON.PK.eq(bean.getPk())); | ||||
| 			LOGGER.debug(sql.toString()); | ||||
| 			TPersonRecord r = sql.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<TCampRecord> sql0 = jooq.selectFrom(T_CAMP).where(T_CAMP.PK.eq(fkCamp)); | ||||
| 			LOGGER.debug(sql0.toString()); | ||||
| 			TCampRecord rc = sql0.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<TPersonRecord> sql1 = jooq | ||||
| 			// @formatter:off | ||||
| 				.update(T_PERSON) | ||||
| 				.set(T_PERSON.FORENAME, bean.getForename()) | ||||
| 				.set(T_PERSON.SURNAME, bean.getSurname()) | ||||
| 				.set(T_PERSON.STREET, bean.getStreet()) | ||||
| 				.set(T_PERSON.ZIP, bean.getZip()) | ||||
| 				.set(T_PERSON.CITY, bean.getCity()) | ||||
| 				.set(T_PERSON.BIRTHDATE, bean.getBirthdate()) | ||||
| 				.set(T_PERSON.SEX, bean.getSex()) | ||||
| 				.set(T_PERSON.PHONE, bean.getPhone()) | ||||
| 				.set(T_PERSON.EMAIL, bean.getEmail()) | ||||
| 				.set(T_PERSON.COMMENT, bean.getComment()) | ||||
| 				.set(T_PERSON.ACCEPT, bean.getAccept()) | ||||
| 				.set(T_PERSON.CAMPROLE, bean.getCamprole()) | ||||
| 				.where(T_PERSON.PK.eq(bean.getPk())); | ||||
| 			// @formatter:on | ||||
| 			LOGGER.debug(sql1.toString()); | ||||
| 			lrw.add(sql1.execute()); | ||||
|  | ||||
| 			// always | ||||
| 			StringBuilder buf = new StringBuilder("Eine Anmeldung für "); | ||||
| 			buf.append(campNameWithYear); | ||||
| 			buf.append(" wurde von ").append(registrator); | ||||
| 			buf.append(" korrigiert."); | ||||
|  | ||||
| 			InsertValuesStep2<TRssRecord, String, String> sql2 = 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(); | ||||
| 		}); | ||||
|  | ||||
| 		// send email to user instead of rss feed | ||||
| 		Boolean acceptNew = lrw.getBoolean("acceptNew"); | ||||
| 		Boolean acceptOld = lrw.getBoolean("acceptOld"); | ||||
| 		String campNameWithYear = lrw.getString("campNameWithYear"); | ||||
| 		String email = lrw.getString("oldEmail"); | ||||
| 		StringBuilder buf = new StringBuilder(); | ||||
| 		if (acceptNew == null) { | ||||
| 			if (acceptOld != null) { | ||||
| 				buf = new StringBuilder("Die Bestätigung der Anmeldung von "); | ||||
| 				buf.append(bean.getForename()); | ||||
| 				buf.append(" "); | ||||
| 				buf.append(bean.getSurname()); | ||||
| 				buf.append(" zur Freizeit "); | ||||
| 				buf.append(campNameWithYear); | ||||
| 				buf.append(" wurde von "); | ||||
| 				buf.append(registrator); | ||||
| 				buf.append(" wieder zurückgezogen."); | ||||
| 				buf.append( | ||||
| 						" Möglicherweise wurde die Anmeldung versehentlich bestätigt? Deine Anmeldung befindet sich jetzt wieder auf der Warteliste."); | ||||
| 			} | ||||
| 		} else if (acceptNew == true) { | ||||
| 			if (acceptOld == null || !acceptOld) { | ||||
| 				buf = new StringBuilder("Die Anmeldung von "); | ||||
| 				buf.append(bean.getForename()); | ||||
| 				buf.append(" "); | ||||
| 				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(" um die Bestätigungen herunterzuladen."); | ||||
| 			} | ||||
| 		} else if (acceptNew == false) { | ||||
| 			if (acceptOld == null || acceptOld) { | ||||
| 				buf = new StringBuilder("Die Anmeldung von "); | ||||
| 				buf.append(bean.getForename()); | ||||
| 				buf.append(" "); | ||||
| 				buf.append(bean.getSurname()); | ||||
| 				buf.append(" zur Freizeit "); | ||||
| 				buf.append(campNameWithYear); | ||||
| 				buf.append(" wurde leider abgelehnt."); | ||||
| 				buf.append( | ||||
| 						" Möglicherweise ist sie schon ausgebucht? Deine Anmeldung befindet sich jetzt auf der Nachrückerliste."); | ||||
| 			} | ||||
| 		} | ||||
| 		Set<String> to = new HashSet<>(); | ||||
| 		to.add(email); | ||||
| 		to.add(bean.getEmail()); | ||||
| 		try { | ||||
| 			mailRepository.sendMail(to, buf.toString()); // no matter if the sending works, do the persistence anyway | ||||
| 		} catch (Exception e) { | ||||
| 			LOGGER.error(e.getMessage(), e); | ||||
| 		} | ||||
| 		return lrw.getCounter(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
|   | ||||
| @@ -23,8 +23,8 @@ public class PersonService implements IPersonService { | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public Integer updatePerson(PersonBean bean) { | ||||
| 		return gateway.updatePerson(bean); | ||||
| 	public Integer updatePerson(PersonBean bean, String worker) { | ||||
| 		return gateway.updatePerson(bean, worker); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
|   | ||||
| @@ -0,0 +1,137 @@ | ||||
| package de.jottyfan.camporganizer.module.dashboard; | ||||
|  | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSON; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSONDOCUMENT; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_RSS; | ||||
|  | ||||
| import org.apache.logging.log4j.LogManager; | ||||
| import org.apache.logging.log4j.Logger; | ||||
| import org.jooq.DSLContext; | ||||
| import org.jooq.DeleteConditionStep; | ||||
| import org.jooq.InsertValuesStep2; | ||||
| import org.jooq.InsertValuesStep4; | ||||
| import org.jooq.UpdateConditionStep; | ||||
| import org.jooq.exception.DataAccessException; | ||||
| import org.jooq.impl.DSL; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Repository; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
|  | ||||
| import de.jottyfan.camporganizer.db.jooq.enums.EnumFiletype; | ||||
| import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord; | ||||
| import de.jottyfan.camporganizer.db.jooq.tables.records.TPersondocumentRecord; | ||||
| import de.jottyfan.camporganizer.db.jooq.tables.records.TRssRecord; | ||||
| import de.jottyfan.camporganizer.module.common.BookingBean; | ||||
| import de.jottyfan.camporganizer.module.common.LambdaResultWrapper; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Repository | ||||
| @Transactional(transactionManager = "transactionManager") | ||||
| public class DashboardGateway { | ||||
| 	private static final Logger LOGGER = LogManager.getLogger(DashboardGateway.class); | ||||
|  | ||||
| 	@Autowired | ||||
| 	private DSLContext jooq; | ||||
|  | ||||
| 	/** | ||||
| 	 * update defined fields of the bean | ||||
| 	 * | ||||
| 	 * @param bean the bean | ||||
| 	 * @return number of affected database rows; should be 1 | ||||
| 	 */ | ||||
| 	public Integer update(BookingBean bean) { | ||||
| 		UpdateConditionStep<TPersonRecord> sql = jooq | ||||
| 		// @formatter:off | ||||
| 			.update(T_PERSON) | ||||
| 			.set(T_PERSON.FORENAME, bean.getForename()) | ||||
| 			.set(T_PERSON.SURNAME, bean.getSurname()) | ||||
| 			.set(T_PERSON.STREET, bean.getStreet()) | ||||
| 			.set(T_PERSON.ZIP, bean.getZip()) | ||||
| 			.set(T_PERSON.CITY, bean.getCity()) | ||||
| 			.set(T_PERSON.PHONE, bean.getPhone()) | ||||
| 			.set(T_PERSON.EMAIL, bean.getEmail()) | ||||
| 			.set(T_PERSON.COMMENT, bean.getComment()) | ||||
| 			.where(T_PERSON.PK.eq(bean.getPk())); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug(sql.toString()); | ||||
| 		return sql.execute(); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	/** | ||||
| 	 * delete entry from t_persondocument where pk = ? | ||||
| 	 * | ||||
| 	 * @param pk | ||||
| 	 *          to be used as reference | ||||
| 	 * @return number of affected database lines | ||||
| 	 * @throws DataAccessException | ||||
| 	 */ | ||||
| 	public Integer deletePersondocument(PersondocumentBean bean) throws DataAccessException { | ||||
| 		LambdaResultWrapper lrw = new LambdaResultWrapper(); | ||||
| 		jooq.transaction(t -> { | ||||
|  | ||||
| 			DeleteConditionStep<TPersondocumentRecord> sql = DSL.using(t) | ||||
| 			// @formatter:off | ||||
| 				.deleteFrom(T_PERSONDOCUMENT) | ||||
| 				.where(T_PERSONDOCUMENT.PK.eq(bean.getPk())); | ||||
| 			// @formatter:on | ||||
| 			LOGGER.debug("{}", sql.toString()); | ||||
| 			lrw.add(sql.execute()); | ||||
|  | ||||
| 			StringBuilder buf = new StringBuilder("Dokument "); | ||||
| 			buf.append(bean.getName()); | ||||
| 			buf.append(" wurde wieder gelöscht."); | ||||
| 			InsertValuesStep2<TRssRecord, String, String> sql2 = 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(); | ||||
| 		}); | ||||
| 		return lrw.getCounter(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * add document to database | ||||
| 	 * | ||||
| 	 * @param bean | ||||
| 	 * @throws DataAccessException | ||||
| 	 */ | ||||
| 	public void addPersondocument(PersondocumentBean bean) throws DataAccessException { | ||||
| 		jooq.transaction(t -> { | ||||
|  | ||||
| 			InsertValuesStep4<TPersondocumentRecord, String, EnumFiletype, Integer, String> sql = DSL.using(t) | ||||
| 		  // @formatter:off | ||||
| 		    	.insertInto(T_PERSONDOCUMENT, | ||||
| 		    			       T_PERSONDOCUMENT.NAME, | ||||
| 		    			       T_PERSONDOCUMENT.FILETYPE, | ||||
| 		    			       T_PERSONDOCUMENT.FK_PERSON, | ||||
| 		    			       T_PERSONDOCUMENT.DOCUMENT | ||||
| 		    			       ) | ||||
| 		    	.values(bean.getName(), bean.getFiletype(), bean.getFkPerson(), bean.getDocument()); | ||||
| 		  // @formatter:on | ||||
| 			LOGGER.debug("{}", sql.toString()); | ||||
| 			sql.execute(); | ||||
|  | ||||
| 			StringBuilder buf = new StringBuilder("Dokument "); | ||||
| 			buf.append(bean.getName()); | ||||
| 			buf.append(" wurde angelegt."); | ||||
| 			InsertValuesStep2<TRssRecord, String, String> sql2 = 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(); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,86 @@ | ||||
| package de.jottyfan.camporganizer.module.dashboard; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.util.Base64; | ||||
|  | ||||
| import javax.servlet.http.Part; | ||||
|  | ||||
| import org.apache.commons.io.IOUtils; | ||||
|  | ||||
| import de.jottyfan.camporganizer.db.jooq.enums.EnumFiletype; | ||||
|  | ||||
| /** | ||||
|  *  | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| public class PersondocumentBean { | ||||
|  | ||||
| 	private final Integer pk; | ||||
| 	private Integer fkPerson; | ||||
| 	private String name; | ||||
| 	private String document; | ||||
| 	private EnumFiletype filetype; | ||||
| 	private Part uploadfile; | ||||
|  | ||||
| 	public PersondocumentBean(Integer pk) { | ||||
| 		this.pk = pk; | ||||
| 	} | ||||
|  | ||||
| 	public void encodeUpload() throws IOException { | ||||
| 		if (uploadfile != null) { | ||||
| 			InputStream inputStream = uploadfile.getInputStream(); | ||||
| 			byte[] bytes = IOUtils.toByteArray(inputStream); | ||||
| 			if (bytes.length > 0) { | ||||
| 				document = Base64.getEncoder().encodeToString(bytes); | ||||
| 			} // not uploaded files should not be changed, so document must be kept as is | ||||
| 		} else { | ||||
| 			throw new IOException("uploadfile is null"); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public Integer getPk() { | ||||
| 		return pk; | ||||
| 	} | ||||
|  | ||||
| 	public void setFkPerson(Integer fkPerson) { | ||||
| 		this.fkPerson = fkPerson; | ||||
| 	} | ||||
|  | ||||
| 	public Integer getFkPerson() { | ||||
| 		return fkPerson; | ||||
| 	} | ||||
|  | ||||
| 	public void setName(String name) { | ||||
| 		this.name = name; | ||||
| 	} | ||||
|  | ||||
| 	public String getName() { | ||||
| 		return name; | ||||
| 	} | ||||
|  | ||||
| 	public void setDocument(String document) { | ||||
| 		this.document = document; | ||||
| 	} | ||||
|  | ||||
| 	public String getDocument() { | ||||
| 		return document; | ||||
| 	} | ||||
|  | ||||
| 	public void setFiletype(EnumFiletype filetype) { | ||||
| 		this.filetype = filetype; | ||||
| 	} | ||||
|  | ||||
| 	public EnumFiletype getFiletype() { | ||||
| 		return filetype; | ||||
| 	} | ||||
|  | ||||
| 	public Part getUploadfile() { | ||||
| 		return uploadfile; | ||||
| 	} | ||||
|  | ||||
| 	public void setUploadfile(Part uploadfile) { | ||||
| 		this.uploadfile = uploadfile; | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,96 @@ | ||||
| package de.jottyfan.camporganizer.module.mail; | ||||
|  | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.Set; | ||||
|  | ||||
| import javax.mail.MessagingException; | ||||
| import javax.mail.internet.MimeMessage; | ||||
|  | ||||
| import org.apache.logging.log4j.LogManager; | ||||
| import org.apache.logging.log4j.Logger; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.mail.javamail.JavaMailSender; | ||||
| import org.springframework.mail.javamail.MimeMessageHelper; | ||||
| import org.springframework.stereotype.Repository; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Repository | ||||
| public class MailRepository { | ||||
|  | ||||
| 	private final static Logger LOGGER = LogManager.getLogger(); | ||||
|  | ||||
| 	@Autowired | ||||
| 	private JavaMailSender javaMailSender; | ||||
|  | ||||
| 	@Value("${spring.mail.username}") | ||||
| 	private String username; | ||||
|  | ||||
| 	/** | ||||
| 	 * Send an email with the message to the recipient. If email is blank, do | ||||
| 	 * nothing | ||||
| 	 * | ||||
| 	 * @param to      the email addresses | ||||
| 	 * @param message the message | ||||
| 	 */ | ||||
| 	public void sendMail(Set<String> to, String message) { | ||||
| 		if (to != null && to.size() > 0) { | ||||
| 			if (username != null && !username.isBlank()) { | ||||
| 				try { | ||||
| 					sendMail(to, message, username); | ||||
| 				} catch (MessagingException e) { | ||||
| 					LOGGER.error(e.getMessage(), e); | ||||
| 				} | ||||
| 			} else { | ||||
| 				LOGGER.error("no email.username in configuration for sending emails"); | ||||
| 			} | ||||
| 		} else { | ||||
| 			LOGGER.warn("no email address given, ignore informing the user about changes; message would have been: {}", | ||||
| 					message); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * send the email | ||||
| 	 * | ||||
| 	 * @param to      the recipients | ||||
| 	 * @param message the message | ||||
| 	 * @param from    the username of the email account | ||||
| 	 * @throws MessagingException | ||||
| 	 */ | ||||
| 	private void sendMail(Set<String> to, String message, String from) throws MessagingException { | ||||
| 		if (to == null || to.size() < 1) { | ||||
| 			throw new MessagingException("no recipient in " + to); | ||||
| 		} | ||||
| 		MimeMessage mimeMessage = javaMailSender.createMimeMessage(); | ||||
| 		MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED, | ||||
| 				StandardCharsets.UTF_8.name()); | ||||
| 		helper.setFrom(from); | ||||
| 		helper.setSubject("Information zu Deiner Anmeldung zur Onkel Werner Freizeit"); | ||||
| 		helper.setText(message, false); | ||||
| 		helper.setTo(to.toArray(new String[] {})); | ||||
| 		javaMailSender.send(mimeMessage); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * for junit tests only | ||||
| 	 * | ||||
| 	 * @param javaMailSender the java mail sender | ||||
| 	 */ | ||||
| 	protected void setJavaMailSender(JavaMailSender javaMailSender) { | ||||
| 		this.javaMailSender = javaMailSender; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * for junit tests only | ||||
| 	 * | ||||
| 	 * @param username the username | ||||
| 	 */ | ||||
| 	protected void setUsername(String username) { | ||||
| 		this.username = username; | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,64 @@ | ||||
| package de.jottyfan.camporganizer.module.registration; | ||||
|  | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| public class ProfileBean implements Serializable { | ||||
| 	private static final long serialVersionUID = 1L; | ||||
|  | ||||
| 	private Integer pk; | ||||
| 	private String forename; | ||||
| 	private String surname; | ||||
| 	private String username; | ||||
|  | ||||
| 	public void clear() { | ||||
| 		this.pk = null; | ||||
| 		this.forename = null; | ||||
| 		this.surname = null; | ||||
| 		this.username = null; | ||||
| 	} | ||||
|  | ||||
| 	public Boolean getIsEmpty() { | ||||
| 		return pk == null; | ||||
| 	} | ||||
|  | ||||
| 	public String getFullname() { | ||||
| 		return new StringBuilder(forename == null ? "" : forename).append(" ").append(surname == null ? "" : surname) | ||||
| 				.toString(); | ||||
| 	} | ||||
|  | ||||
| 	public String getUsername() { | ||||
| 		return username; | ||||
| 	} | ||||
|  | ||||
| 	public void setUsername(String username) { | ||||
| 		this.username = username; | ||||
| 	} | ||||
|  | ||||
| 	public String getForename() { | ||||
| 		return forename; | ||||
| 	} | ||||
|  | ||||
| 	public void setForename(String forename) { | ||||
| 		this.forename = forename; | ||||
| 	} | ||||
|  | ||||
| 	public String getSurname() { | ||||
| 		return surname; | ||||
| 	} | ||||
|  | ||||
| 	public void setSurname(String surname) { | ||||
| 		this.surname = surname; | ||||
| 	} | ||||
| 	public Integer getPk() { | ||||
| 		return pk; | ||||
| 	} | ||||
|  | ||||
| 	public void setPk(Integer pk) { | ||||
| 		this.pk = pk; | ||||
| 	} | ||||
| } | ||||
| @@ -49,6 +49,13 @@ public class RegistrationBean implements Serializable { | ||||
| 	private String login; | ||||
| 	private String password; | ||||
|  | ||||
| 	/** | ||||
| 	 * @return forename + surname, separated by a space | ||||
| 	 */ | ||||
| 	public String getFullname() { | ||||
| 		return new StringBuilder().append(forename).append(" ").append(surname).toString(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the forename | ||||
| 	 */ | ||||
|   | ||||
| @@ -4,9 +4,12 @@ import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSON; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSONDOCUMENT; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_PROFILE; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_PROFILEROLE; | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_RSS; | ||||
|  | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalDateTime; | ||||
| import java.time.format.DateTimeFormatter; | ||||
| import java.time.temporal.ChronoUnit; | ||||
| import java.util.UUID; | ||||
|  | ||||
| @@ -18,11 +21,17 @@ import org.jooq.DeleteConditionStep; | ||||
| import org.jooq.InsertResultStep; | ||||
| import org.jooq.InsertValuesStep12; | ||||
| import org.jooq.InsertValuesStep13; | ||||
| import org.jooq.InsertValuesStep2; | ||||
| import org.jooq.Record; | ||||
| import org.jooq.Record1; | ||||
| import org.jooq.Record2; | ||||
| import org.jooq.Record5; | ||||
| import org.jooq.Record7; | ||||
| import org.jooq.SelectConditionStep; | ||||
| import org.jooq.UpdateConditionStep; | ||||
| import org.jooq.exception.DataAccessException; | ||||
| import org.jooq.impl.DSL; | ||||
| import org.jooq.types.DayToSecond; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Repository; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
| @@ -33,6 +42,7 @@ 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.TPersondocumentRecord; | ||||
| import de.jottyfan.camporganizer.db.jooq.tables.records.TProfileRecord; | ||||
| import de.jottyfan.camporganizer.db.jooq.tables.records.TRssRecord; | ||||
| import de.jottyfan.camporganizer.module.common.BookingBean; | ||||
| import de.jottyfan.camporganizer.module.common.LambdaResultWrapper; | ||||
|  | ||||
| @@ -102,7 +112,31 @@ public class RegistrationGateway { | ||||
| 				if (bean.getRegisterInKeycloak() && !loginNotYetInUse) { | ||||
| 					throw new DataAccessException("login already in use: " + bean.getLogin()); | ||||
| 				} | ||||
| 				// TODO: check if teacher is at least 2 years older than the camp participants | ||||
|  | ||||
| 				// TODO: move to bean validator instead: | ||||
| 				// check for valid birthdate of teachers | ||||
| 				LocalDateTime birthDate = bean.getBirthDate().atStartOfDay(); | ||||
|  | ||||
| 				if (EnumCamprole.teacher.equals(bean.getCampRole())) { | ||||
| 					SelectConditionStep<Record2<Integer, DayToSecond>> 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()); | ||||
| @@ -120,6 +154,16 @@ public class RegistrationGateway { | ||||
| 					// @formatter:on | ||||
| 					LOGGER.debug(sql1.toString()); | ||||
| 					fkProfile = sql1.fetchOne().getPk(); | ||||
|  | ||||
| 					InsertValuesStep2<TRssRecord, String, String> sql2 = jooq | ||||
| 					// @formatter:off | ||||
| 					  .insertInto(T_RSS, | ||||
| 					  		        T_RSS.MSG, | ||||
| 					  		        T_RSS.RECIPIENT) | ||||
| 					  .values(new StringBuilder(bean.getFullname()).append(" hat sich als Nutzer im CampOrganizer2 registriert.").toString(), "admin"); | ||||
| 					// @formatter:on | ||||
| 					LOGGER.debug("{}", sql2.toString()); | ||||
| 					sql2.execute(); | ||||
| 				} else { | ||||
| 					SelectConditionStep<Record1<Integer>> sql1 = DSL.using(t) | ||||
| 					// @formatter:off | ||||
| @@ -228,6 +272,32 @@ public class RegistrationGateway { | ||||
| 	public Integer removeBooking(Integer id) { | ||||
| 		LambdaResultWrapper lrw = new LambdaResultWrapper(); | ||||
| 		jooq.transaction(t -> { | ||||
| 			SelectConditionStep<Record5<String, String, String, String, LocalDateTime>> sql0 = DSL.using(t) | ||||
| 			// @formatter:off | ||||
| 				.select(T_PROFILE.USERNAME, T_PERSON.FORENAME, T_PERSON.SURNAME, T_CAMP.NAME, T_CAMP.ARRIVE) | ||||
| 				.from(T_PERSON) | ||||
| 				.leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_PERSON.FK_CAMP)) | ||||
| 				.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_PERSON.FK_PROFILE)) | ||||
| 				.where(T_PERSON.PK.eq(id)); | ||||
| 			// @formatter:on | ||||
| 			LOGGER.debug(sql0.toString()); | ||||
| 			Record5<String, String, String, String, LocalDateTime> r = sql0.fetchOne(); | ||||
| 			if (r == null) { | ||||
| 				throw new DataAccessException("no such entry in t_person with id = " + id); | ||||
| 			} | ||||
| 			String username = r.get(T_PROFILE.USERNAME); | ||||
| 			String forename = r.get(T_PERSON.FORENAME); | ||||
| 			String surname = r.get(T_PERSON.SURNAME); | ||||
| 			String campname = r.get(T_CAMP.NAME); | ||||
| 			LocalDateTime arrive = r.get(T_CAMP.ARRIVE); | ||||
|  | ||||
| 			StringBuilder rssMessage = new StringBuilder(username); | ||||
| 			rssMessage.append(" hat die Buchung von "); | ||||
| 			rssMessage.append(forename).append(" ").append(surname); | ||||
| 			rssMessage.append(" an "); | ||||
| 			rssMessage.append(campname).append(" ").append(arrive == null ? "" : arrive.format(DateTimeFormatter.ofPattern("YYYY"))); | ||||
| 			rssMessage.append(" storniert."); | ||||
|  | ||||
| 			DeleteConditionStep<TPersondocumentRecord> sql1 = DSL.using(t).deleteFrom(T_PERSONDOCUMENT) | ||||
| 					.where(T_PERSONDOCUMENT.FK_PERSON.eq(id)); | ||||
| 			LOGGER.debug(sql1.toString()); | ||||
| @@ -236,6 +306,16 @@ public class RegistrationGateway { | ||||
| 			DeleteConditionStep<TPersonRecord> sql2 = DSL.using(t).deleteFrom(T_PERSON).where(T_PERSON.PK.eq(id)); | ||||
| 			LOGGER.debug(sql2.toString()); | ||||
| 			lrw.add(sql2.execute()); | ||||
|  | ||||
| 			InsertValuesStep2<TRssRecord, String, String> sql3 = DSL.using(t) | ||||
| 			// @formatter:off | ||||
| 				.insertInto(T_RSS, | ||||
|                     T_RSS.MSG, | ||||
|                     T_RSS.RECIPIENT) | ||||
| 				.values(rssMessage.toString(), "registrator"); | ||||
| 			// @formatter:on | ||||
| 			LOGGER.debug("{}", sql3.toString()); | ||||
| 			sql3.execute(); | ||||
| 		}); | ||||
| 		return lrw.getCounter(); | ||||
| 	} | ||||
| @@ -261,4 +341,55 @@ public class RegistrationGateway { | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * remove login | ||||
| 	 * | ||||
| 	 * @param bean | ||||
| 	 *          containing username of dataset to be removed | ||||
| 	 * @throws DataAccessExceptionF | ||||
| 	 */ | ||||
| 	public void removeLogin(ProfileBean bean) throws DataAccessException { | ||||
| 		jooq.transaction(t -> { | ||||
| 			UpdateConditionStep<TPersonRecord> sql = DSL.using(t) | ||||
| 			// @formatter:off | ||||
| 			  .update(T_PERSON) | ||||
| 			  .set(T_PERSON.FK_PROFILE, (Integer) null) | ||||
| 			  .where(T_PERSON.FK_PROFILE.eq(bean.getPk())); | ||||
| 			// @formatter:off | ||||
| 			LOGGER.debug("{}", sql.toString()); | ||||
| 			sql.execute(); | ||||
|  | ||||
| 			DeleteConditionStep<?> sql1 = DSL.using(t) | ||||
| 			// @formatter:off | ||||
| 		    .deleteFrom(T_PROFILEROLE) | ||||
| 		    .where(T_PROFILEROLE.FK_PROFILE.in( | ||||
| 		    		DSL.using(t) | ||||
| 		    		.select(T_PROFILE.PK) | ||||
| 		    		.from(T_PROFILE) | ||||
| 		      .where(T_PROFILE.USERNAME.eq(bean.getUsername()) | ||||
| 		    ))); | ||||
| 			// @formatter:on | ||||
| 			LOGGER.debug("{}", sql1.toString()); | ||||
| 			sql1.execute(); | ||||
|  | ||||
| 			DeleteConditionStep<?> sql2 = DSL.using(t) | ||||
| 			// @formatter:off | ||||
| 		    .deleteFrom(T_PROFILE) | ||||
| 		    .where(T_PROFILE.USERNAME.eq(bean.getUsername())); | ||||
| 			// @formatter:on | ||||
| 			LOGGER.debug("{}", sql2.toString()); | ||||
| 			sql2.execute(); | ||||
|  | ||||
| 			InsertValuesStep2<TRssRecord, String, String> sql3 = DSL.using(t) | ||||
| 			// @formatter:off | ||||
| 				.insertInto(T_RSS, | ||||
| 						        T_RSS.MSG, | ||||
| 						        T_RSS.RECIPIENT) | ||||
| 				.values(new StringBuilder(bean.getFullname()).append(" hat sich vom Portal CampOrganizer2 abgemeldet.").toString(), "admin"); | ||||
| 			// @formatter:on | ||||
| 			LOGGER.debug("{}", sql3.toString()); | ||||
| 			sql3.execute(); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,54 @@ | ||||
| package de.jottyfan.camporganizer.module.rss; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| public class RssBean implements Serializable { | ||||
| 	private static final long serialVersionUID = 1L; | ||||
|  | ||||
| 	public final Integer pk; | ||||
| 	public String recipient; | ||||
| 	public String message; | ||||
| 	public LocalDateTime pubdate; | ||||
|  | ||||
| 	public RssBean(Integer pk) { | ||||
| 		this.pk = pk; | ||||
| 	} | ||||
|  | ||||
| 	public String getMessage80() { | ||||
| 		return message == null ? null : (message.length() > 80 ? message.substring(0, 80).concat("...") : message); | ||||
| 	} | ||||
|  | ||||
| 	public Integer getPk() { | ||||
| 		return pk; | ||||
| 	} | ||||
|  | ||||
| 	public String getMessage() { | ||||
| 		return message; | ||||
| 	} | ||||
|  | ||||
| 	public void setMessage(String message) { | ||||
| 		this.message = message; | ||||
| 	} | ||||
|  | ||||
| 	public LocalDateTime getPubdate() { | ||||
| 		return pubdate; | ||||
| 	} | ||||
|  | ||||
| 	public void setPubdate(LocalDateTime pubdate) { | ||||
| 		this.pubdate = pubdate; | ||||
| 	} | ||||
|  | ||||
| 	public String getRecipient() { | ||||
| 		return recipient; | ||||
| 	} | ||||
|  | ||||
| 	public void setRecipient(String recipient) { | ||||
| 		this.recipient = recipient; | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,55 @@ | ||||
| package de.jottyfan.camporganizer.module.rss; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.PrintWriter; | ||||
| import java.time.LocalDateTime; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
|  | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Controller; | ||||
| import org.springframework.web.bind.annotation.GetMapping; | ||||
|  | ||||
| import com.rometools.rome.feed.synd.SyndFeed; | ||||
| import com.rometools.rome.io.FeedException; | ||||
| import com.rometools.rome.io.SyndFeedOutput; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Controller | ||||
| public class RssController { | ||||
|  | ||||
| 	private String recipientCode; | ||||
|  | ||||
| 	@Autowired | ||||
| 	private RssService service; | ||||
|  | ||||
| 	@GetMapping("/rss") | ||||
| 	public String toRss(HttpServletResponse response) throws IOException, FeedException { | ||||
| 		List<RssBean> beans = new ArrayList<>(); | ||||
| 		if (recipientCode != null) { | ||||
| 			beans = service.getRss(recipientCode); | ||||
| 		} else { | ||||
| 			RssBean bean = new RssBean(null); | ||||
| 			bean.setPubdate(LocalDateTime.now()); | ||||
| 			bean.setMessage("Dieser Feed ist nicht mehr aktuell. Bitte gib einen recipientCode an."); | ||||
| 			beans.add(bean); | ||||
| 		} | ||||
| 		SyndFeed feed = new RssModel().getRss(beans); | ||||
| 		response.reset(); | ||||
| 		response.setCharacterEncoding("UTF-8"); | ||||
| 		response.setContentType("application/rss+xml"); | ||||
| 		response.setHeader("Content-Disposition", "attachment; filename=\"onkelwernerfreizeiten.de.xml\""); | ||||
| 		PrintWriter writer; | ||||
| 		writer = response.getWriter(); | ||||
| 		SyndFeedOutput output = new SyndFeedOutput(); | ||||
| 		output.output(feed, writer); | ||||
| 		response.flushBuffer(); | ||||
| 		return "error"; | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,101 @@ | ||||
| package de.jottyfan.camporganizer.module.rss; | ||||
|  | ||||
| import static de.jottyfan.camporganizer.db.jooq.Tables.T_RSS; | ||||
|  | ||||
| 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.DeleteConditionStep; | ||||
| import org.jooq.Record3; | ||||
| import org.jooq.Record4; | ||||
| import org.jooq.SelectConditionStep; | ||||
| import org.jooq.SelectJoinStep; | ||||
| import org.jooq.UpdateConditionStep; | ||||
| import org.jooq.exception.DataAccessException; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Repository; | ||||
| import org.springframework.transaction.annotation.Transactional; | ||||
|  | ||||
| import de.jottyfan.camporganizer.db.jooq.tables.records.TRssRecord; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Repository | ||||
| @Transactional(transactionManager = "transactionManager") | ||||
| public class RssGateway { | ||||
|  | ||||
| 	private static final Logger LOGGER = LogManager.getLogger(RssGateway.class); | ||||
|  | ||||
| 	@Autowired | ||||
| 	private DSLContext jooq; | ||||
|  | ||||
| 	public List<RssBean> getRss(String recipientCode) throws DataAccessException { | ||||
| 		SelectConditionStep<Record3<Integer, String, LocalDateTime>> sql = jooq | ||||
| 		// @formatter:off | ||||
| 		  .select(T_RSS.PK, | ||||
| 	  		      T_RSS.MSG, | ||||
| 		  	      T_RSS.REGDATE) | ||||
| 		  .from(T_RSS) | ||||
| 		  .where(T_RSS.RECIPIENT.eq(recipientCode)); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug("{}", sql.toString()); | ||||
| 		List<RssBean> list = new ArrayList<>(); | ||||
| 		for (Record3<Integer, String, LocalDateTime> r : sql.fetch()) { | ||||
| 			RssBean bean = new RssBean(r.get(T_RSS.PK)); | ||||
| 			bean.setRecipient(recipientCode); | ||||
| 			bean.setMessage(r.get(T_RSS.MSG)); | ||||
| 			bean.setPubdate(r.get(T_RSS.REGDATE)); | ||||
| 			list.add(bean); | ||||
| 		} | ||||
| 		return list; | ||||
| 	} | ||||
|  | ||||
| 	public List<RssBean> getAllRss() throws DataAccessException { | ||||
| 		SelectJoinStep<Record4<Integer, String, String, LocalDateTime>> sql = jooq | ||||
| 		// @formatter:off | ||||
| 		  .select(T_RSS.PK, | ||||
| 	  		      T_RSS.RECIPIENT, | ||||
| 	  		      T_RSS.MSG, | ||||
| 		  	      T_RSS.REGDATE) | ||||
| 		  .from(T_RSS); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug("{}", sql.toString()); | ||||
| 		List<RssBean> list = new ArrayList<>(); | ||||
| 		for (Record4<Integer, String, String, LocalDateTime> r : sql.fetch()) { | ||||
| 			RssBean bean = new RssBean(r.get(T_RSS.PK)); | ||||
| 			bean.setRecipient(r.get(T_RSS.RECIPIENT)); | ||||
| 			bean.setMessage(r.get(T_RSS.MSG)); | ||||
| 			bean.setPubdate(r.get(T_RSS.REGDATE)); | ||||
| 			list.add(bean); | ||||
| 		} | ||||
| 		return list; | ||||
| 	} | ||||
|  | ||||
| 	public void deleteRss(RssBean bean) throws DataAccessException { | ||||
| 		DeleteConditionStep<TRssRecord> sql = jooq | ||||
| 		// @formatter:off | ||||
| 			.deleteFrom(T_RSS) | ||||
| 			.where(T_RSS.PK.eq(bean.getPk())); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug("{}", sql.toString()); | ||||
| 		sql.execute(); | ||||
| 	} | ||||
|  | ||||
| 	public void update(RssBean bean) throws DataAccessException { | ||||
| 		UpdateConditionStep<TRssRecord> sql = jooq | ||||
| 		// @formatter:off | ||||
| 			.update(T_RSS) | ||||
| 			.set(T_RSS.MSG, bean.getMessage()) | ||||
| 			.where(T_RSS.PK.eq(bean.getPk())); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug("{}", sql.toString()); | ||||
| 		sql.execute(); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,44 @@ | ||||
| package de.jottyfan.camporganizer.module.rss; | ||||
|  | ||||
| import java.sql.Timestamp; | ||||
| import java.text.SimpleDateFormat; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import com.rometools.rome.feed.synd.SyndContent; | ||||
| import com.rometools.rome.feed.synd.SyndContentImpl; | ||||
| import com.rometools.rome.feed.synd.SyndEntry; | ||||
| import com.rometools.rome.feed.synd.SyndEntryImpl; | ||||
| import com.rometools.rome.feed.synd.SyndFeed; | ||||
| import com.rometools.rome.feed.synd.SyndFeedImpl; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| public class RssModel { | ||||
| 	public SyndFeed getRss(List<RssBean> beans) { | ||||
| 		SyndFeed feed = new SyndFeedImpl(); | ||||
| 		feed.setFeedType("rss_2.0"); | ||||
| 		feed.setTitle("Onkel Werner Freizeiten e.V. Anmeldungsnotifier"); | ||||
| 		feed.setLink("https://www.onkelwernerfreizeiten.de/camporganizer/rss.jsf"); | ||||
| 		feed.setDescription("In diesem Feed werden Portalaktivitäten gesammelt."); | ||||
| 		feed.setEncoding("UTF-8"); | ||||
| 		List<SyndEntry> entries = new ArrayList<>(); | ||||
| 		for (RssBean bean : beans) { | ||||
| 				SyndEntry entry = new SyndEntryImpl(); | ||||
| 				entry.setTitle("neue Aktivität"); | ||||
| 				entry.setLink("https://www.onkelwernerfreizeiten.de/camporganizer/"); | ||||
| 				entry.setUri(new SimpleDateFormat("yyyyMMddHHmmssSSS").format(bean.getPubdate())); | ||||
| 				entry.setPublishedDate(Timestamp.valueOf(bean.getPubdate())); | ||||
| 				SyndContent description = new SyndContentImpl(); | ||||
| 				description.setType("text/plain"); | ||||
| 				description.setValue(bean.getMessage()); | ||||
| 				entry.setDescription(description); | ||||
| 				entries.add(entry); | ||||
| 		} | ||||
| 		feed.setEntries(entries); | ||||
| 		return feed; | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| package de.jottyfan.camporganizer.module.rss; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Service | ||||
| public class RssService { | ||||
| 	@Autowired | ||||
| 	private RssGateway repository; | ||||
|  | ||||
| 	/** | ||||
| 	 * get the recipient's rss feed | ||||
| 	 * @param recipientCode the code for the feed | ||||
| 	 * @return the list of rss beans; an empty list at least | ||||
| 	 */ | ||||
| 	public List<RssBean> getRss(String recipientCode) { | ||||
| 		return repository.getRss(recipientCode); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -18,6 +18,16 @@ keycloak.use-resource-role-mappings = ${keycloak.use-resource-role-mappings} | ||||
| ow.keycloak.admin.name = ${ow.keycloak.admin.name} | ||||
| ow.keycloak.admin.password = ${ow.keycloak.admin.password} | ||||
|  | ||||
| spring.mail.default-encoding = ${spring.mail.default-encoding} | ||||
| spring.mail.host = ${spring.mail.host} | ||||
| spring.mail.username = ${spring.mail.username} | ||||
| spring.mail.password = ${spring.mail.password} | ||||
| spring.mail.port = ${spring.mail.port} | ||||
| spring.mail.protocol = ${spring.mail.protocol} | ||||
| spring.mail.test-connection = ${spring.mail.test-connection} | ||||
| spring.mail.properties.mail.smtp.auth = ${spring.mail.properties.mail.smtp.auth} | ||||
| spring.mail.properties.mail.smtp.starttls.enable = ${spring.mail.properties.mail.smtp.starttls.enable} | ||||
|  | ||||
| # for development only | ||||
| server.port = 8081 | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user