keycloak registration done
This commit is contained in:
		| @@ -25,19 +25,19 @@ import org.springframework.stereotype.Repository; | ||||
| public class KeycloakRepository { | ||||
| 	private final static Logger LOGGER = LogManager.getLogger(KeycloakRepository.class); | ||||
|  | ||||
| 	@Value("${keycloak.resource:biblecamp}") | ||||
| 	@Value("${keycloak.resource}") | ||||
| 	private String keycloakClientId; | ||||
|  | ||||
| 	@Value("${keycloak.auth-server-url}") | ||||
| 	private String keycloakUrl; | ||||
|  | ||||
| 	@Value("${keycloak.realm:ow}") | ||||
| 	@Value("${keycloak.realm}") | ||||
| 	private String keycloakRealm; | ||||
|  | ||||
| 	@Value("${ow.keycloak.admin.name:admin") | ||||
| 	@Value("${ow.keycloak.admin.name}") | ||||
| 	private String keycloakAdminName; | ||||
|  | ||||
| 	@Value("${ow.keycloak.admin.password:password") | ||||
| 	@Value("${ow.keycloak.admin.password}") | ||||
| 	private String keycloakAdminPassword; | ||||
|  | ||||
| 	/** | ||||
| @@ -54,28 +54,32 @@ public class KeycloakRepository { | ||||
| 	/** | ||||
| 	 * register the login in keycloak | ||||
| 	 * | ||||
| 	 * @param forename the forename | ||||
| 	 * @param surname  the surname | ||||
| 	 * @param login    the username | ||||
| 	 * @param password the password | ||||
| 	 * @param email    the email | ||||
| 	 * @return true or false | ||||
| 	 */ | ||||
| 	public boolean register(String login, String password, String email) { | ||||
| 		UserRepresentation user = getUserRepresentation(login, password, email); | ||||
| 		UsersResource resource = getUsersResource(keycloakUrl, keycloakRealm, keycloakAdminName, keycloakAdminPassword, keycloakClientId); | ||||
| 		boolean result = register(resource, user); | ||||
| 		sendVerificationLink(login, resource); | ||||
| 		return result; | ||||
| 	public boolean register(String forename, String surname, String login, String password, String email) { | ||||
| 		UserRepresentation user = getUserRepresentation(forename, surname, login, password, email); | ||||
| 		UsersResource resource = getUsersResource(keycloakUrl, keycloakRealm, keycloakAdminName, keycloakAdminPassword, | ||||
| 				keycloakClientId); | ||||
| 		return register(resource, user); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * generate a user representation | ||||
| 	 * | ||||
| 	 * @param login the login | ||||
| 	 * @param forename the forename | ||||
| 	 * @param surname  the surname | ||||
| 	 * @param login    the login | ||||
| 	 * @param password the password | ||||
| 	 * @param email the email | ||||
| 	 * @param email    the email | ||||
| 	 * @return the user representation | ||||
| 	 */ | ||||
| 	protected UserRepresentation getUserRepresentation(String login, String password, String email) { | ||||
| 	protected UserRepresentation getUserRepresentation(String forename, String surname, String login, String password, | ||||
| 			String email) { | ||||
| 		CredentialRepresentation passwordCredentials = new CredentialRepresentation(); | ||||
| 		passwordCredentials.setTemporary(false); | ||||
| 		passwordCredentials.setType(CredentialRepresentation.PASSWORD); | ||||
| @@ -83,6 +87,8 @@ public class KeycloakRepository { | ||||
|  | ||||
| 		UserRepresentation user = new UserRepresentation(); | ||||
| 		user.setUsername(login); | ||||
| 		user.setFirstName(forename); | ||||
| 		user.setLastName(surname); | ||||
| 		user.setEmail(email); | ||||
| 		user.setCredentials(Collections.singletonList(passwordCredentials)); | ||||
| 		user.setEnabled(true); | ||||
| @@ -94,37 +100,42 @@ public class KeycloakRepository { | ||||
| 	 * register a user in keycloak | ||||
| 	 * | ||||
| 	 * @param resource the resource | ||||
| 	 * @param user the user | ||||
| 	 * @param user     the user | ||||
| 	 * @return true or false | ||||
| 	 */ | ||||
| 	protected boolean register(UsersResource resource, UserRepresentation user) { | ||||
| 		Response response = resource.create(user); | ||||
| 		LOGGER.info("created new keycloak user {}", user.getUsername()); | ||||
| 		return Status.OK.equals(response.getStatusInfo()); | ||||
| 		Boolean success = Status.CREATED.equals(response.getStatusInfo()); | ||||
| 		if (success) { | ||||
| 			LOGGER.info("created new keycloak user {}", user.getUsername()); | ||||
| 		} else { | ||||
| 			LOGGER.error("error on creating keycloak user {}: {}", user.getUsername(), response.getStatus()); | ||||
| 		} | ||||
| 		return success; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * generate a keycloak object with the special arguments | ||||
| 	 * | ||||
| 	 * @param url the url | ||||
| 	 * @param realm the realm | ||||
| 	 * @param admin the admin of the realm | ||||
| 	 * @param url      the url | ||||
| 	 * @param realm    the realm | ||||
| 	 * @param admin    the admin of the realm | ||||
| 	 * @param password the password | ||||
| 	 * @param clientId the client ID | ||||
| 	 * @return the keycloak object | ||||
| 	 */ | ||||
| 	public KeycloakBuilder getKeycloak(String url, String realm, String admin, String password, String clientId) { | ||||
| 		return KeycloakBuilder.builder().serverUrl(url).realm(realm).grantType(OAuth2Constants.PASSWORD) | ||||
| 				.username(admin).password(password).clientId(clientId) | ||||
| 		return KeycloakBuilder.builder().serverUrl(url).realm(realm).grantType(OAuth2Constants.PASSWORD).username(admin) | ||||
| 				.password(password).clientId(clientId) | ||||
| 				.resteasyClient(new ResteasyClientBuilderImpl().connectionPoolSize(10).build()); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * generate a users resource object | ||||
| 	 * | ||||
| 	 * @param url the url | ||||
| 	 * @param realm the realm | ||||
| 	 * @param admin the admin of the realm | ||||
| 	 * @param url      the url | ||||
| 	 * @param realm    the realm | ||||
| 	 * @param admin    the admin of the realm | ||||
| 	 * @param password the password | ||||
| 	 * @param clientId the client ID | ||||
| 	 * @return the users resource | ||||
| @@ -138,8 +149,9 @@ public class KeycloakRepository { | ||||
| 	 * | ||||
| 	 * @param userId the ID of the user | ||||
| 	 */ | ||||
| 	public void sendVerificationLink(String userId, UsersResource usersResource) { | ||||
| 		usersResource.get(userId).sendVerifyEmail(); | ||||
| 	public void sendVerificationLink(String userId) { | ||||
| 		getUsersResource(keycloakUrl, keycloakRealm, keycloakAdminName, keycloakAdminPassword, keycloakClientId).get(userId) | ||||
| 				.sendVerifyEmail(); | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -37,20 +37,32 @@ public class RegistrationController extends CommonController { | ||||
| 		bean.setFkCamp(fkCamp); | ||||
| 		bean.setRegisterInKeycloak(true); // we want people to register generally | ||||
| 		model.addAttribute("bean", bean); | ||||
| 		return "/registration"; | ||||
| 		return "/registration/registration"; | ||||
| 	} | ||||
|  | ||||
| 	@PostMapping("/registration/register") | ||||
| 	public String register(@Valid @ModelAttribute RegistrationBean bean, final BindingResult bindingResult, Model model) { | ||||
| 	public String register(@Valid @ModelAttribute("bean") RegistrationBean bean, final BindingResult bindingResult, Model model) { | ||||
| 		super.setupSession(model, request); | ||||
| 		if (bindingResult.hasErrors()) { | ||||
| 			super.setupSession(model, request); | ||||
| 			CampBean campBean = service.getCamp(bean.getFkCamp()); | ||||
| 			model.addAttribute("camp", campBean); | ||||
| 			model.addAttribute("bean", bean); | ||||
| 			return "/registration"; | ||||
| 			return "/registration/registration"; | ||||
| 		} | ||||
| 		Boolean result = service.register(bean); | ||||
| 		// TODO: give the user a message about success or error and, if registered in keycloak, a note about how to login | ||||
| 		return index(bean.getFkCamp(), model); | ||||
| 		return result ? "/registration/success" : "/error"; | ||||
| 	} | ||||
|  | ||||
| 	@GetMapping("/registration/cancel/{id}") | ||||
| 	public String cancellation(@PathVariable Integer id, final Model model) { | ||||
| 		super.setupSession(model, request); | ||||
| 		model.addAttribute("bean", service.getBooking(id)); | ||||
| 		return "/registration/cancellation"; | ||||
| 	} | ||||
|  | ||||
| 	@GetMapping("/registration/remove/{id}") | ||||
| 	public String remove(@PathVariable Integer id, final Model model) { | ||||
| 		super.setupSession(model, request); | ||||
| 		service.removeBooking(id); | ||||
| 		return "redirect:/dashboard"; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package de.jottyfan.camporganizer.module.registration; | ||||
|  | ||||
| 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 java.time.LocalDate; | ||||
| @@ -13,9 +14,11 @@ import org.apache.logging.log4j.LogManager; | ||||
| import org.apache.logging.log4j.Logger; | ||||
| import org.jasypt.util.password.StrongPasswordEncryptor; | ||||
| import org.jooq.DSLContext; | ||||
| import org.jooq.DeleteConditionStep; | ||||
| import org.jooq.InsertResultStep; | ||||
| import org.jooq.InsertValuesStep12; | ||||
| import org.jooq.InsertValuesStep13; | ||||
| import org.jooq.Record7; | ||||
| import org.jooq.SelectConditionStep; | ||||
| import org.jooq.exception.DataAccessException; | ||||
| import org.jooq.impl.DSL; | ||||
| @@ -27,7 +30,9 @@ import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole; | ||||
| import de.jottyfan.camporganizer.db.jooq.enums.EnumSex; | ||||
| 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.module.common.BookingBean; | ||||
| import de.jottyfan.camporganizer.module.common.LambdaResultWrapper; | ||||
|  | ||||
| /** | ||||
| @@ -163,4 +168,61 @@ public class RegistrationGateway { | ||||
| 		}); | ||||
| 		return lrw.getCounter() > 0; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get the booking from the database | ||||
| 	 * | ||||
| 	 * @param id the id of the booking | ||||
| 	 * @return the booking bean or null | ||||
| 	 */ | ||||
| 	public BookingBean getBooking(Integer id) { | ||||
| 		SelectConditionStep<Record7<Integer, String, String, EnumCamprole, String, LocalDateTime, LocalDateTime>> sql = jooq | ||||
| 		// @formatter:off | ||||
| 			.select(T_PERSON.PK, | ||||
| 					    T_PERSON.FORENAME, | ||||
| 					    T_PERSON.SURNAME, | ||||
| 					    T_PERSON.CAMPROLE, | ||||
| 					    T_CAMP.NAME, | ||||
| 					    T_CAMP.ARRIVE, | ||||
| 					    T_CAMP.DEPART) | ||||
| 			.from(T_PERSON) | ||||
| 			.leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_PERSON.FK_CAMP)) | ||||
| 			.where(T_PERSON.PK.eq(id)); | ||||
| 		// @formatter:on | ||||
| 		LOGGER.debug(sql.toString()); | ||||
| 		Record7<Integer, String, String, EnumCamprole, String, LocalDateTime, LocalDateTime> r = sql.fetchOne(); | ||||
| 		if (r != null) { | ||||
| 			BookingBean bean = new BookingBean(); | ||||
| 			bean.setPk(r.get(T_PERSON.PK)); | ||||
| 			bean.setForename(r.get(T_PERSON.FORENAME)); | ||||
| 			bean.setSurname(r.get(T_PERSON.SURNAME)); | ||||
| 			bean.setCampName(r.get(T_CAMP.NAME)); | ||||
| 			bean.setArrive(r.get(T_CAMP.ARRIVE)); | ||||
| 			bean.setDepart(r.get(T_CAMP.DEPART)); | ||||
| 			bean.setCamprole(r.get(T_PERSON.CAMPROLE)); | ||||
| 			return bean; | ||||
| 		} | ||||
| 		return null; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * remove the booking and all of its dependencies | ||||
| 	 * | ||||
| 	 * @param id the pk of t_person | ||||
| 	 * @return number of affected database rows, should be 1 | ||||
| 	 */ | ||||
| 	public Integer removeBooking(Integer id) { | ||||
| 		LambdaResultWrapper lrw = new LambdaResultWrapper(); | ||||
| 		jooq.transaction(t -> { | ||||
| 			DeleteConditionStep<TPersondocumentRecord> sql1 = DSL.using(t).deleteFrom(T_PERSONDOCUMENT) | ||||
| 					.where(T_PERSONDOCUMENT.FK_PERSON.eq(id)); | ||||
| 			LOGGER.debug(sql1.toString()); | ||||
| 			sql1.execute(); | ||||
|  | ||||
| 			DeleteConditionStep<TPersonRecord> sql2 = DSL.using(t).deleteFrom(T_PERSON).where(T_PERSON.PK.eq(id)); | ||||
| 			LOGGER.debug(sql2.toString()); | ||||
| 			lrw.add(sql2.execute()); | ||||
| 		}); | ||||
| 		return lrw.getCounter(); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,12 @@ | ||||
| package de.jottyfan.camporganizer.module.registration; | ||||
|  | ||||
| import org.apache.logging.log4j.LogManager; | ||||
| import org.apache.logging.log4j.Logger; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| import de.jottyfan.camporganizer.module.common.BookingBean; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
| @@ -10,6 +14,7 @@ import org.springframework.stereotype.Service; | ||||
|  */ | ||||
| @Service | ||||
| public class RegistrationService { | ||||
| 	private final static Logger LOGGER = LogManager.getLogger(RegistrationService.class); | ||||
|  | ||||
| 	@Autowired | ||||
| 	private RegistrationGateway gateway; | ||||
| @@ -36,8 +41,34 @@ public class RegistrationService { | ||||
| 	public Boolean register(RegistrationBean bean) { | ||||
| 		Boolean result = gateway.register(bean); | ||||
| 		if (result && bean.getRegisterInKeycloak()) { | ||||
| 			keycloak.register(bean.getLogin(), bean.getPassword(), bean.getEmail()); | ||||
| 			keycloak.register(bean.getForename(), bean.getSurname(), bean.getLogin(), bean.getPassword(), bean.getEmail()); | ||||
| 			if (bean.getEmail() != null && !bean.getEmail().isBlank()) { | ||||
| 				try { | ||||
| 					keycloak.sendVerificationLink(bean.getLogin()); | ||||
| 				} catch (Exception e) { | ||||
| 					LOGGER.error(e.getMessage(), e); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * get the booking bean | ||||
| 	 * | ||||
| 	 * @param id the id of the registration (t_person.pk) | ||||
| 	 * @return the booking bean or null | ||||
| 	 */ | ||||
| 	public BookingBean getBooking(Integer id) { | ||||
| 		return gateway.getBooking(id); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * remove the booking and all of its dependencies | ||||
| 	 * | ||||
| 	 * @param id the id of the booking (t_person.pk) | ||||
| 	 */ | ||||
| 	public Boolean removeBooking(Integer id) { | ||||
| 		return gateway.removeBooking(id) > 0; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,22 +1,22 @@ | ||||
| # database credentials from defined config file | ||||
| spring.config.import=/etc/CampOrganizer2/prod.properties | ||||
| spring.config.import = /etc/CampOrganizer2/prod.properties | ||||
|  | ||||
| # define overwriteable arguments | ||||
| spring.datasource.driver-class-name=${spring.datasource.driver-class-name:org.postgresql.Driver} | ||||
| spring.datasource.url=${spring.datasource.url} | ||||
| spring.datasource.username=${spring.datasource.username} | ||||
| spring.datasource.password=${spring.datasource.password} | ||||
| spring.datasource.driver-class-name = ${spring.datasource.driver-class-name:org.postgresql.Driver} | ||||
| spring.datasource.url = ${spring.datasource.url} | ||||
| spring.datasource.username = ${spring.datasource.username} | ||||
| spring.datasource.password = ${spring.datasource.password} | ||||
|  | ||||
| server.servlet.context-path=${server.servlet.context-path:/CampOrganizer2} | ||||
| server.servlet.context-path = ${server.servlet.context-path:/CampOrganizer2} | ||||
|  | ||||
| keycloak.auth-server-url=${keycloak.auth-server-url} | ||||
| keycloak.realm=${keycloak.realm:ow} | ||||
| keycloak.resource=${keycloak.resource:biblecamp} | ||||
| keycloak.public-client=${keycloak.public-client} | ||||
| keycloak.use-resource-role-mappings=${keycloak.use-resource-role-mappings} | ||||
| keycloak.auth-server-url = ${keycloak.auth-server-url} | ||||
| keycloak.realm = ${keycloak.realm:ow} | ||||
| keycloak.resource = ${keycloak.resource:biblecamp} | ||||
| keycloak.public-client = ${keycloak.public-client} | ||||
| 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} | ||||
| ow.keycloak.admin.name = ${ow.keycloak.admin.name} | ||||
| ow.keycloak.admin.password = ${ow.keycloak.admin.password} | ||||
|  | ||||
| # for development only | ||||
| server.port = 8081 | ||||
|   | ||||
| @@ -28,7 +28,7 @@ | ||||
| 			</script> | ||||
| 			<div class="accordion" id="acc"> | ||||
| 				<div class="accordion-item" th:each="b : ${mybookings}"> | ||||
| 					<h2 class="accordion-header" th:id="'acc-head-' + ${b.pk}"> | ||||
| 					<h2 class="accordion-header" th:id="'acc-head-' + ${b.pk}" th:if="${b.pk}"> | ||||
| 						<button th:class="'accordion-button collapsed acc_' + ${b.accept}" type="button" data-bs-toggle="collapse" th:data-bs-target="'#acc-body-' + ${b.pk}" aria-expanded="true" | ||||
| 							th:aria-controls="'#acc-body-' + ${b.pk}"> | ||||
| 							<i class="fas fa-check framed framed-green" th:if="${b.accept}"></i> <i class="fas fa-ban framed framed-red" th:if="${b.accept} == false"></i> <i | ||||
| @@ -37,7 +37,7 @@ | ||||
| 								th:text="${b.locationName}"></span> | ||||
| 						</button> | ||||
| 					</h2> | ||||
| 					<div th:id="'acc-body-' + ${b.pk}" class="accordion-collapse collapse" th:aria-labelledby="'acc-head-' + ${b.pk}"> | ||||
| 					<div th:id="'acc-body-' + ${b.pk}" class="accordion-collapse collapse" th:aria-labelledby="'acc-head-' + ${b.pk}" th:if="${b.pk}"> | ||||
| 						<div class="accordion-body"> | ||||
| 							<div class="card"> | ||||
| 								<div class="card-header">Freizeitdaten</div> | ||||
| @@ -112,7 +112,7 @@ | ||||
| 											</div> | ||||
| 											<div class="row mb-2"> | ||||
| 												<div class="col-sm-2">Foto-Einverständnis:</div> | ||||
| 												<span class="col-sm-10" th:text="${b.consentCatalogPhoto ? 'ja' : 'nein'}"></span> | ||||
| 												<span class="col-sm-10"><span th:text="${b.consentCatalogPhoto ? 'ja' : 'nein'}" th:if="${b.consentCatalogPhoto}"></span></span> | ||||
| 											</div> | ||||
| 											<div class="row mb-2"> | ||||
| 												<div class="col-sm-2">Kommentar:</div> | ||||
| @@ -120,9 +120,12 @@ | ||||
| 											</div> | ||||
| 											<div class="row mb-2"> | ||||
| 												<div class="col-sm-2"></div> | ||||
| 												<div class="col-sm-10"> | ||||
| 												<div class="col-sm-8"> | ||||
| 													<input type="submit" class="btn btn-primary" value="Änderungen übernehmen" /> | ||||
| 												</div> | ||||
| 												<div class="col-sm-2"> | ||||
| 													<a th:href="@{/registration/cancel/{id}(id=${b.pk})}" class="btn btn-outline-danger">stornieren</a> | ||||
| 												</div> | ||||
| 											</div> | ||||
| 										</div> | ||||
| 									</form> | ||||
|   | ||||
| @@ -13,7 +13,7 @@ | ||||
| 		</ul> | ||||
| 		<ul class="navbar-nav mb-2 mb-lg-0"> | ||||
| 			<li class="nav-item"> | ||||
| 				<a class="btn btn-icon-silent menufont"	href="http://anmeldung.onkelwernerfreizeiten.de">Unsere Freizeiten</a> | ||||
| 				<a class="btn btn-icon-silent menufont"	th:href="@{/}">Unsere Freizeiten</a> | ||||
| 			</li> | ||||
| 		</ul> | ||||
| 	</th:block> | ||||
| @@ -56,7 +56,7 @@ | ||||
| 					<div class="row"> | ||||
| 						<div class="col-sm-3"></div> | ||||
| 						<div class="col-sm-9"> | ||||
| 							<a class="btn btn-primary buttonfont" href="http://anmeldung.onkelwernerfreizeiten.de">jetzt anmelden</a> | ||||
| 							<a class="btn btn-primary buttonfont" th:href="@{/registration/{id}(id=${c.pk})}">jetzt anmelden</a> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 				</div> | ||||
|   | ||||
							
								
								
									
										44
									
								
								src/main/resources/templates/registration/cancellation.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/main/resources/templates/registration/cancellation.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| <!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 2</title> | ||||
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||||
| </head> | ||||
| <body> | ||||
| 	<th:block layout:fragment="header"> | ||||
| 		<ul class="navbar-nav mb-2 mb-lg-0"> | ||||
| 			<li class="nav-item"><a th:href="@{${keycloakProfileUrl}}" class="btn btn-secondary btn-icon-silent" target="_blank">Profil</a></li> | ||||
| 		</ul> | ||||
| 		<ul class="navbar-nav mb-2 mb-lg-0"> | ||||
| 			<li class="nav-item"><a th:href="@{/business}" class="btn btn-secondary btn-icon-silent" sec:authorize="hasRole('business')">Abrechnung</a></li> | ||||
| 		</ul> | ||||
| 		<ul class="navbar-nav mb-2 mb-lg-0"> | ||||
| 			<li class="nav-item"><a th:href="@{/confirmation}" class="btn btn-secondary btn-icon-silent" sec:authorize="hasRole('registrator')">Bestätigung</a></li> | ||||
| 		</ul> | ||||
| 		<ul class="navbar-nav mb-2 mb-lg-0"> | ||||
| 			<li class="nav-item"><a href="https://www.onkelwernerfreizeiten.de/cloud" class="btn btn-secondary btn-icon-silent" target="_blank">Nextcloud</a></li> | ||||
| 		</ul> | ||||
| 	</th:block> | ||||
| 	<th:block layout:fragment="content"> | ||||
| 		<div class="mainpage"> | ||||
| 			<div class="alert alert-warning"> | ||||
| 				<span th:if="${bean.isStudent}">Willst du deine Anmeldung wirklich stornieren und damit den Freizeitplatz für einen anderen Teilnehmer freigeben? Diese Stornierung kann nicht | ||||
| 					rückgängig gemacht werden.</span> <span th:if="${!bean.isStudent}">Willst du deine Anmeldung wirklich stornieren?</span> | ||||
| 			</div> | ||||
| 			<div class="card"> | ||||
| 				<div class="card-header"> | ||||
| 					<span th:text="${bean.campName + ' ' + #temporals.format(bean.arrive, 'dd.MM.') + ' - ' + #temporals.format(bean.depart, 'dd.MM.yyyy')}" | ||||
| 						th:if="${bean.arrive != null and bean.depart != null}"></span> | ||||
| 				</div> | ||||
| 				<div class="card-body"> | ||||
| 					<span th:text="${bean.forename + ' ' + bean.surname}"></span>, <span th:if="${bean.isStudent}">Teilnehmer</span><span th:if="${bean.isTeacher}">Mitarbeiter</span> <span | ||||
| 						th:if="${bean.isDirector}">Leiter</span><span th:if="${bean.isFeeder}">Küchenhilfe</span> | ||||
| 				</div> | ||||
| 				<div class="card-footer"> | ||||
| 					<a th:href="@{/registration/remove/{id}(id=${bean.pk})}" class="btn btn-danger">Ja, stornieren</a>  <a th:href="@{/dashboard}" class="btn btn-outline-success">Stornierung abbrechen</a> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</th:block> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										31
									
								
								src/main/resources/templates/registration/success.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/main/resources/templates/registration/success.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| <!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 2</title> | ||||
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||||
| </head> | ||||
| <body> | ||||
| 	<th:block layout:fragment="header"> | ||||
| 		<ul class="navbar-nav mb-2 mb-lg-0"> | ||||
| 			<li class="nav-item"><a th:href="@{${keycloakProfileUrl}}" class="btn btn-secondary btn-icon-silent" target="_blank">Profil</a></li> | ||||
| 		</ul> | ||||
| 		<ul class="navbar-nav mb-2 mb-lg-0"> | ||||
| 			<li class="nav-item"><a th:href="@{/business}" class="btn btn-secondary btn-icon-silent" sec:authorize="hasRole('business')">Abrechnung</a></li> | ||||
| 		</ul> | ||||
| 		<ul class="navbar-nav mb-2 mb-lg-0"> | ||||
| 			<li class="nav-item"><a th:href="@{/confirmation}" class="btn btn-secondary btn-icon-silent" sec:authorize="hasRole('registrator')">Bestätigung</a></li> | ||||
| 		</ul> | ||||
| 		<ul class="navbar-nav mb-2 mb-lg-0"> | ||||
| 			<li class="nav-item"><a href="https://www.onkelwernerfreizeiten.de/cloud" class="btn btn-secondary btn-icon-silent" target="_blank">Nextcloud</a></li> | ||||
| 		</ul> | ||||
| 	</th:block> | ||||
| 	<th:block layout:fragment="content"> | ||||
| 		<div class="mainpage"> | ||||
| 			<div class="alert alert-success"> | ||||
| 				Deine Anmeldung wurde entgegengenommen. Falls du dir auch ein Login eingerichtet hast, kannst du dich jetzt oben rechts einloggen und deine Anmeldung bearbeiten. | ||||
| 			</div> | ||||
| 			<div><a th:href="@{/}" class="btn btn-outline-secondary">zur Hauptseite</a></div> | ||||
| 		</div> | ||||
| 	</th:block> | ||||
| </body> | ||||
| </html> | ||||
		Reference in New Issue
	
	Block a user