show current camps on start page
This commit is contained in:
		| @@ -55,8 +55,12 @@ public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { | ||||
| 	@Override | ||||
| 	protected void configure(HttpSecurity http) throws Exception { | ||||
| 		super.configure(http); | ||||
| 		http.authorizeRequests().antMatchers("/**").permitAll(); | ||||
| 		http.anonymous().disable(); | ||||
| 		http.authorizeRequests() | ||||
| 		// @formatter:off | ||||
| 		  .antMatchers("/user/**", "/business/**").authenticated() | ||||
|   		.anyRequest().permitAll(); | ||||
|   	// @formatter:on | ||||
| //		http.anonymous().disable(); | ||||
| 		http.csrf().disable(); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,37 @@ | ||||
| package de.jottyfan.camporganizer.module.common; | ||||
|  | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
|  | ||||
| import org.jooq.exception.DataAccessException; | ||||
| import org.keycloak.KeycloakSecurityContext; | ||||
| import org.springframework.ui.Model; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| public abstract class CommonController { | ||||
|  | ||||
| 	/** | ||||
| 	 * try to get current keycloak user | ||||
| 	 * | ||||
| 	 * @param request the request | ||||
| 	 * @return the preferred username or null | ||||
| 	 */ | ||||
| 	private String getCurrentUser(HttpServletRequest request) { | ||||
| 		KeycloakSecurityContext ksc = (KeycloakSecurityContext) request | ||||
| 				.getAttribute(KeycloakSecurityContext.class.getName()); | ||||
| 		return ksc == null ? null : ksc.getIdToken().getPreferredUsername(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * setup the session for the template | ||||
| 	 * @param model the model | ||||
| 	 * @param request the request | ||||
| 	 */ | ||||
| 	public void setupSession(Model model, HttpServletRequest request) { | ||||
| 		String username = getCurrentUser(request); | ||||
| 		model.addAttribute("currentUser", username); | ||||
| 	} | ||||
| } | ||||
| @@ -7,6 +7,7 @@ import org.apache.logging.log4j.LogManager; | ||||
| import org.apache.logging.log4j.Logger; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Controller; | ||||
| import org.springframework.ui.Model; | ||||
| import org.springframework.web.bind.annotation.GetMapping; | ||||
|  | ||||
| /** | ||||
| @@ -15,18 +16,27 @@ import org.springframework.web.bind.annotation.GetMapping; | ||||
|  * | ||||
|  */ | ||||
| @Controller | ||||
| public class IndexController { | ||||
| public class IndexController extends CommonController { | ||||
| 	private static final Logger LOGGER = LogManager.getLogger(IndexController.class); | ||||
|  | ||||
| 	@Autowired | ||||
| 	private HttpServletRequest request; | ||||
|  | ||||
| 	@Autowired | ||||
| 	private IndexService service; | ||||
|  | ||||
| 	@GetMapping("/") | ||||
| 	public String index() { | ||||
| 		if (request == null) { | ||||
| 			LOGGER.error("request is null"); | ||||
| 		} | ||||
| 		return "/"; | ||||
| 	public String index(Model model) { | ||||
| 		super.setupSession(model, request); | ||||
| 		model.addAttribute("camps", service.getAllCamps()); | ||||
| 		return "/index"; | ||||
| 	} | ||||
|  | ||||
| 	@GetMapping("/user") | ||||
| 	public String user(Model model) { | ||||
| 		super.setupSession(model, request); | ||||
| 		model.addAttribute("camps", service.getAllCamps()); | ||||
| 		return "/user/index"; | ||||
| 	} | ||||
|  | ||||
| 	@GetMapping("/logout") | ||||
|   | ||||
| @@ -0,0 +1,33 @@ | ||||
| package de.jottyfan.camporganizer.module.common; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.stream.Stream; | ||||
|  | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Service; | ||||
|  | ||||
| import de.jottyfan.camporganizer.db.jooq.tables.records.VCampRecord; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Service | ||||
| public class IndexService { | ||||
| 	@Autowired | ||||
| 	private IndexGateway gateway; | ||||
|  | ||||
| 	/** | ||||
| 	 * get all camps from the database and prepare them for the view | ||||
| 	 * | ||||
| 	 * @return the list of found camps | ||||
| 	 */ | ||||
| 	public List<VCampRecord> getAllCamps() { | ||||
| 		Stream<VCampRecord> stream = gateway.getAllCamps(); | ||||
| 		List<VCampRecord> list = new ArrayList<>(); | ||||
| 		stream.forEach(o -> list.add(o)); | ||||
| 		return list; | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,25 @@ | ||||
| package de.jottyfan.camporganizer.module.common; | ||||
|  | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.boot.info.BuildProperties; | ||||
| import org.springframework.stereotype.Component; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * @author jotty | ||||
|  * | ||||
|  */ | ||||
| @Component | ||||
| public class ManifestBean { | ||||
|  | ||||
| 	@Autowired(required = false) | ||||
| 	private BuildProperties buildProperties; | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the version of this project | ||||
| 	 */ | ||||
| 	public String getVersion() { | ||||
| 		return buildProperties != null ? buildProperties.getVersion() | ||||
| 				: this.getClass().getPackage().getImplementationVersion(); | ||||
| 	} | ||||
| } | ||||
| @@ -7,7 +7,7 @@ spring.datasource.password=jooq | ||||
| # application | ||||
| server.port = 8081 | ||||
|  | ||||
| server.servlet.context-path=/COBusiness | ||||
| server.servlet.context-path=/CampOrganizer2 | ||||
|  | ||||
| # keycloak | ||||
| keycloak.auth-server-url = http://localhost:8080/ | ||||
| @@ -15,7 +15,7 @@ keycloak.realm = ow | ||||
| keycloak.resource = biblecamp | ||||
| keycloak.public-client = true | ||||
| keycloak.security-constraints[0].authRoles[0] = business | ||||
| keycloak.security-constraints[0].securityCollections[0].patterns[0] = /* | ||||
| keycloak.security-constraints[0].securityCollections[0].patterns[0] = /business/* | ||||
| #keycloak.credentia | ||||
| keycloak.use-resource-role-mappings=true | ||||
| #keycloak.bearer-only=true | ||||
|   | ||||
							
								
								
									
										100
									
								
								src/main/resources/static/css/style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/main/resources/static/css/style.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| html { | ||||
| 	width: 100vw; | ||||
| 	height: 100vh; | ||||
| } | ||||
|  | ||||
| body { | ||||
| 	background-image: linear-gradient(to bottom right, rgb(255, 190, 111), rgb(198, 70, 0)); | ||||
| } | ||||
|  | ||||
| .mainpage { | ||||
| 	background-color: rgba(255, 255, 255, 0.4); | ||||
| 	padding: 8px; | ||||
| 	overflow: auto; | ||||
| 	width: 100%; | ||||
| 	height: calc(100vh - 60px); /* 60 px is the current height of the headline; TODO: calculate it */ | ||||
| } | ||||
|  | ||||
| .middlecenter { | ||||
| 	margin: auto; | ||||
| 	top: 40vh; | ||||
| 	bottom: 40vh; | ||||
| 	position: fixed; | ||||
| 	left: 40vw; | ||||
| 	right: 40vw; | ||||
| } | ||||
|  | ||||
| .myheadline { | ||||
| 	margin: 8px; | ||||
| } | ||||
|  | ||||
| .bottomdist16 { | ||||
| 	margin-bottom: 16px; | ||||
| } | ||||
|  | ||||
| .topright { | ||||
| 	position: fixed; | ||||
| 	right: 8px; | ||||
| 	top: 8px; | ||||
| } | ||||
|  | ||||
| .tablelink { | ||||
| 	cursor: pointer; | ||||
| 	text-decoration: none; | ||||
| 	color: black; | ||||
| } | ||||
|  | ||||
| .tablelink:hover { | ||||
| 	text-decoration: underline; | ||||
| 	color: darkcyan; | ||||
| } | ||||
|  | ||||
| .deletelink { | ||||
| 	padding: 4px; | ||||
| 	border: 1px solid rgba(0, 0, 0, 0.0); | ||||
| 	border-radius: 4px; | ||||
| } | ||||
|  | ||||
| .deletelink:hover { | ||||
| 	color: red; | ||||
| 	border: 1px solid red; | ||||
| 	text-decoration-line: line-through; | ||||
| } | ||||
|  | ||||
| .linkbtn { | ||||
| 	background: transparent; | ||||
| 	border: 2px solid transparent; | ||||
| 	padding: 8px; | ||||
| } | ||||
|  | ||||
| .linkbtn:hover { | ||||
| 	background-color: white; | ||||
| 	border: 2px solid silver; | ||||
| 	border-radius: 4px; | ||||
| } | ||||
|  | ||||
| .btn-icon-silent { | ||||
| 	background: transparent; | ||||
| 	border: 2px solid transparent; | ||||
| 	padding: 8px; | ||||
| 	color: silver; | ||||
| } | ||||
|  | ||||
| .btn-icon-silent:hover { | ||||
| 	background-color: white; | ||||
| 	border: 2px solid silver; | ||||
| 	border-radius: 4px; | ||||
| 	color: darkcyan; | ||||
| } | ||||
|  | ||||
| .mytoggle_collapsed { | ||||
| 	display: none; | ||||
| } | ||||
|  | ||||
| .mytoggle_btn { | ||||
| 	cursor: pointer; | ||||
| } | ||||
|  | ||||
| .mytoggle_btn:hover { | ||||
| 	background-color: #abcdef; | ||||
| } | ||||
							
								
								
									
										8
									
								
								src/main/resources/static/js/mytoggle.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/main/resources/static/js/mytoggle.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| class MyToggle { | ||||
| 	constructor() { | ||||
| 	} | ||||
|  | ||||
| 	toggle(divid) { | ||||
| 		$("[id='" + divid + "']").toggle(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										20
									
								
								src/main/resources/templates/error.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/main/resources/templates/error.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| <!DOCTYPE html> | ||||
| <html xmlns:th="http://www.thymeleaf.org"> | ||||
|  | ||||
| <head> | ||||
| <title>Fehler</title> | ||||
| <link th:rel="stylesheet" th:href="@{/css/style.css}" /> | ||||
| <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/bootstrap/5.1.3/css/bootstrap.min.css} " /> | ||||
| <script th:src="@{/webjars/bootstrap/5.1.3/js/bootstrap.min.js}"></script> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|   <div class="middlecenter"> | ||||
|     <div class="alert alert-danger" role="alert"> | ||||
|       <p>Es ist ein Fehler aufgetreten.</p> | ||||
|       <a th:href="@{/}" class="linkbtn">Ach, Mist...</a> | ||||
|     </div> | ||||
|   </div> | ||||
| </body> | ||||
|  | ||||
| </html> | ||||
							
								
								
									
										61
									
								
								src/main/resources/templates/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/main/resources/templates/index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| <!DOCTYPE html> | ||||
| <html th:replace="~{template :: layout(~{::title}, ~{::libs}, ~{::header}, ~{::content})}" xmlns:th="http://www.thymeleaf.org" 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" /> | ||||
| <libs></libs> | ||||
| </head> | ||||
| <body> | ||||
| 	<header> | ||||
| 		<a th:href="@{/}" class="btn btn-secondary btn-icon-silent" title="aktualisieren"><i class="fas fa-sync"></i></a> | ||||
| 		<span>Das Buchungsportal dess Onkel Werner Freizeiten e.V.</span> | ||||
| 	</header> | ||||
| 	<content> | ||||
| 	<div class="mainpage"> | ||||
| 		<script type="text/javascript"> | ||||
| 			var mytoggle = new MyToggle(); | ||||
| 		</script> | ||||
| 		<h1>Unsere Freizeiten</h1> | ||||
| 		<div class="card bottomdist16" th:each="c : ${camps}"> | ||||
| 			<div class="card-header mytoggle_btn" th:onclick="mytoggle.toggle('campdiv_[[${c.pk}]]')"> | ||||
| 				<span th:text="${c.name}"></span> <span th:text="${#numbers.formatInteger(c.year, 0)}" th:if="${c.year != null}"></span> | ||||
| 			</div> | ||||
| 			<div th:id="'campdiv_' + ${c.pk}" class="card-body mytoggle_collapsed"> | ||||
| 				<div class="container"> | ||||
| 					<div class="row"> | ||||
| 						<div class="col-sm-3">Ort</div> | ||||
| 						<div class="col-sm-9"> | ||||
| 							<a th:href="${c.url}" th:text="${c.locationName}" target="_blank"></a> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 					<div class="row"> | ||||
| 						<div class="col-sm-3">Jungen und Mädchen</div> | ||||
| 						<div class="col-sm-9" th:text="${c.minAge} + ' - ' + ${c.maxAge}"></div> | ||||
| 					</div> | ||||
| 					<div class="row"> | ||||
| 						<div class="col-sm-3">Zeit</div> | ||||
| 						<div class="col-sm-9"> | ||||
| 							<span th:text="${#temporals.format(c.arrive, 'dd.MM.')} + ' - ' + ${#temporals.format(c.depart, 'dd.MM.yyyy')}" th:if="${c.arrive != null &&  c.depart != null}"></span> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 					<div class="row"> | ||||
| 						<div class="col-sm-3">Preis</div> | ||||
| 						<div class="col-sm-9" th:text="${c.price}"></div> | ||||
| 					</div> | ||||
| 					<div class="row"> | ||||
| 						<div class="col-sm-3">Ferien</div> | ||||
| 						<div class="col-sm-9" th:text="${c.countries}"></div> | ||||
| 					</div> | ||||
| 					<div class="row"> | ||||
| 						<div class="col-sm-3"></div> | ||||
| 						<div class="col-sm-9"> | ||||
| 							<button class="btn btn-primary">jetzt anmelden</button> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	</content> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										33
									
								
								src/main/resources/templates/template.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/main/resources/templates/template.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| <!DOCTYPE html> | ||||
| <html th:fragment="layout (title, libs, header, content)" xmlns:th="http://www.thymeleaf.org"> | ||||
| <head> | ||||
| <title th:replace="${title}">Camp Organizer Business</title> | ||||
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||||
| <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/bootstrap/5.2.0/css/bootstrap.min.css} " /> | ||||
| <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/font-awesome/5.15.4/css/all.css} " /> | ||||
| <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/datatables/1.11.4/css/jquery.dataTables.min.css}" /> | ||||
| <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/webjars/select2/4.0.13/css/select2.min.css}" /> | ||||
| <link th:rel="stylesheet" type="text/css" media="all" th:href="@{/css/style.css}" /> | ||||
| <script th:src="@{/webjars/jquery/3.6.0/jquery.min.js}"></script> | ||||
| <script th:src="@{/webjars/bootstrap/5.2.0/js/bootstrap.bundle.min.js}"></script> | ||||
| <script th:src="@{/webjars/datatables/1.11.4/js/jquery.dataTables.min.js}"></script> | ||||
| <script th:src="@{/webjars/select2/4.0.13/js/select2.full.min.js}"></script> | ||||
| <script th:src="@{/js/dataTables.de.js}"></script> | ||||
| <script th:src="@{/js/mytoggle.js}"></script> | ||||
| <meta th:replace="${libs}"></meta> | ||||
| </head> | ||||
| <body> | ||||
| 	<div class="myheadline"> | ||||
| 		<div class="topright linkbtn"> | ||||
| 			<span th:if="${not #strings.isEmpty(currentUser)}"> | ||||
| 				<b th:inline="text">[[${currentUser}]]</b> aus Version <span th:text="${@manifestBean.getVersion()}"></span> <a th:href="@{/logout}">ausloggen</a> | ||||
| 			</span> | ||||
| 			<span th:if="${#strings.isEmpty(currentUser)}"> | ||||
| 				<a th:href="@{/user}">einloggen</a> | ||||
| 			</span> | ||||
| 		</div> | ||||
| 		<span th:replace="${header}">Layout header</span> | ||||
| 	</div> | ||||
| 	<div th:replace="${content}">Layout content</div> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										61
									
								
								src/main/resources/templates/user/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/main/resources/templates/user/index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| <!DOCTYPE html> | ||||
| <html th:replace="~{template :: layout(~{::title}, ~{::libs}, ~{::header}, ~{::content})}" xmlns:th="http://www.thymeleaf.org" 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" /> | ||||
| <libs></libs> | ||||
| </head> | ||||
| <body> | ||||
| 	<header> | ||||
| 		<a th:href="@{/}" class="btn btn-secondary btn-icon-silent" title="aktualisieren"><i class="fas fa-sync"></i></a> | ||||
| 		<span>Das Buchungsportal dess Onkel Werner Freizeiten e.V.</span> | ||||
| 	</header> | ||||
| 	<content> | ||||
| 	<div class="mainpage"> | ||||
| 		<script type="text/javascript"> | ||||
| 			var mytoggle = new MyToggle(); | ||||
| 		</script> | ||||
| 		<h1>Unsere Freizeiten</h1> | ||||
| 		<div class="card bottomdist16" th:each="c : ${camps}"> | ||||
| 			<div class="card-header mytoggle_btn" th:onclick="mytoggle.toggle('campdiv_[[${c.pk}]]')"> | ||||
| 				<span th:text="${c.name}"></span> <span th:text="${#numbers.formatInteger(c.year, 0)}" th:if="${c.year != null}"></span> | ||||
| 			</div> | ||||
| 			<div th:id="'campdiv_' + ${c.pk}" class="card-body mytoggle_collapsed"> | ||||
| 				<div class="container"> | ||||
| 					<div class="row"> | ||||
| 						<div class="col-sm-3">Ort</div> | ||||
| 						<div class="col-sm-9"> | ||||
| 							<a th:href="${c.url}" th:text="${c.locationName}" target="_blank"></a> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 					<div class="row"> | ||||
| 						<div class="col-sm-3">Jungen und Mädchen</div> | ||||
| 						<div class="col-sm-9" th:text="${c.minAge} + ' - ' + ${c.maxAge}"></div> | ||||
| 					</div> | ||||
| 					<div class="row"> | ||||
| 						<div class="col-sm-3">Zeit</div> | ||||
| 						<div class="col-sm-9"> | ||||
| 							<span th:text="${#temporals.format(c.arrive, 'dd.MM.')} + ' - ' + ${#temporals.format(c.depart, 'dd.MM.yyyy')}" th:if="${c.arrive != null &&  c.depart != null}"></span> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 					<div class="row"> | ||||
| 						<div class="col-sm-3">Preis</div> | ||||
| 						<div class="col-sm-9" th:text="${c.price}"></div> | ||||
| 					</div> | ||||
| 					<div class="row"> | ||||
| 						<div class="col-sm-3">Ferien</div> | ||||
| 						<div class="col-sm-9" th:text="${c.countries}"></div> | ||||
| 					</div> | ||||
| 					<div class="row"> | ||||
| 						<div class="col-sm-3"></div> | ||||
| 						<div class="col-sm-9"> | ||||
| 							<button class="btn btn-primary">jetzt anmelden</button> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	</content> | ||||
| </body> | ||||
| </html> | ||||
		Reference in New Issue
	
	Block a user