show current camps on start page
This commit is contained in:
parent
d15a6af941
commit
cfe4edcf15
@ -55,8 +55,12 @@ public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
|
|||||||
@Override
|
@Override
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
super.configure(http);
|
super.configure(http);
|
||||||
http.authorizeRequests().antMatchers("/**").permitAll();
|
http.authorizeRequests()
|
||||||
http.anonymous().disable();
|
// @formatter:off
|
||||||
|
.antMatchers("/user/**", "/business/**").authenticated()
|
||||||
|
.anyRequest().permitAll();
|
||||||
|
// @formatter:on
|
||||||
|
// http.anonymous().disable();
|
||||||
http.csrf().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.apache.logging.log4j.Logger;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,18 +16,27 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Controller
|
@Controller
|
||||||
public class IndexController {
|
public class IndexController extends CommonController {
|
||||||
private static final Logger LOGGER = LogManager.getLogger(IndexController.class);
|
private static final Logger LOGGER = LogManager.getLogger(IndexController.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private HttpServletRequest request;
|
private HttpServletRequest request;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IndexService service;
|
||||||
|
|
||||||
@GetMapping("/")
|
@GetMapping("/")
|
||||||
public String index() {
|
public String index(Model model) {
|
||||||
if (request == null) {
|
super.setupSession(model, request);
|
||||||
LOGGER.error("request is null");
|
model.addAttribute("camps", service.getAllCamps());
|
||||||
}
|
return "/index";
|
||||||
return "/";
|
}
|
||||||
|
|
||||||
|
@GetMapping("/user")
|
||||||
|
public String user(Model model) {
|
||||||
|
super.setupSession(model, request);
|
||||||
|
model.addAttribute("camps", service.getAllCamps());
|
||||||
|
return "/user/index";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/logout")
|
@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
|
# application
|
||||||
server.port = 8081
|
server.port = 8081
|
||||||
|
|
||||||
server.servlet.context-path=/COBusiness
|
server.servlet.context-path=/CampOrganizer2
|
||||||
|
|
||||||
# keycloak
|
# keycloak
|
||||||
keycloak.auth-server-url = http://localhost:8080/
|
keycloak.auth-server-url = http://localhost:8080/
|
||||||
@ -15,7 +15,7 @@ keycloak.realm = ow
|
|||||||
keycloak.resource = biblecamp
|
keycloak.resource = biblecamp
|
||||||
keycloak.public-client = true
|
keycloak.public-client = true
|
||||||
keycloak.security-constraints[0].authRoles[0] = business
|
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.credentia
|
||||||
keycloak.use-resource-role-mappings=true
|
keycloak.use-resource-role-mappings=true
|
||||||
#keycloak.bearer-only=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>
|
Loading…
x
Reference in New Issue
Block a user