10 Commits

Author SHA1 Message Date
12a325ee03 corrected username check and dropdown size, see #57 2023-09-21 21:32:18 +02:00
497a4dfcf9 registration screen optimized, see #58 2023-09-21 19:54:23 +02:00
f31c433c80 bugfix: allow spaces after user names on login from old registrations 2023-08-27 21:58:40 +02:00
7732d6da25 corrected keycloak urls 2023-08-06 16:05:18 +02:00
b07de9f2bc determine mail correctly 2023-08-06 15:43:44 +02:00
e8354c3f6e jakartarized 2023-08-06 00:10:01 +02:00
4a2d743a23 version display improved 2023-07-29 17:48:41 +02:00
89cfbc6741 see #48 2023-05-13 12:29:25 +02:00
5b861f730f added filter to privilege dropdowns 2023-05-06 20:39:34 +02:00
d1ee923f0a see #45 2023-05-06 17:44:14 +02:00
106 changed files with 1664 additions and 821 deletions

View File

@ -1,25 +1,20 @@
buildscript { plugins {
ext { id 'org.springframework.boot' version '3.1.1'
springBootVersion = '2.7.4' id "io.spring.dependency-management" version "1.1.2"
} id 'java'
repositories { id 'war'
mavenCentral() id 'eclipse'
} id 'application'
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
} }
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'war'
apply plugin: 'application'
group = 'de.jottyfan.camporganizer' group = 'de.jottyfan.camporganizer'
version = '0.5.0' version = '0.5.7'
description = """CampOrganizer2"""
sourceCompatibility = 17 sourceCompatibility = 17
targetCompatibility = 17
mainClassName = "de.jottyfan.camporganizer.Main" mainClassName = "de.jottyfan.camporganizer.Main"
repositories { repositories {
@ -27,16 +22,21 @@ repositories {
maven { maven {
url "https://www.jottyfan.de/libs/" url "https://www.jottyfan.de/libs/"
} }
} maven {
url "https://repo.maven.apache.org/maven2"
ext { }
set('keycloakVersion', '21.1.1')
} }
war { war {
manifest { doFirst {
attributes("Implementation-Version": version) manifest {
attributes("Implementation-Title": project.name,
"Implementation-Version": version,
"Implementation-Timestamp": new Date())
}
} }
baseName = project.name
version = version
archiveName = 'CampOrganizer2.war' archiveName = 'CampOrganizer2.war'
} }
@ -54,11 +54,10 @@ dependencies {
implementation 'net.sf.biweekly:biweekly:0.6.6' implementation 'net.sf.biweekly:biweekly:0.6.6'
implementation 'org.keycloak:keycloak-spring-boot-starter'
// for using the keycloak rest interface // for using the keycloak rest interface
implementation 'org.keycloak:keycloak-admin-client:21.1.1' implementation 'org.keycloak:keycloak-server-spi:22.0.1'
implementation 'org.jboss.resteasy:resteasy-client:5.0.0.Final' implementation 'org.keycloak:keycloak-admin-client:22.0.0'
implementation 'org.jboss.resteasy:resteasy-client:6.2.4.Final'
// backward compatibility until the complete registration is converted to keycloak // backward compatibility until the complete registration is converted to keycloak
implementation 'org.jasypt:jasypt:1.9.3' implementation 'org.jasypt:jasypt:1.9.3'
@ -72,19 +71,27 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jooq' implementation 'org.springframework.boot:spring-boot-starter-jooq'
implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-security'
implementation "org.springframework.boot:spring-boot-starter-oauth2-client"
implementation 'org.springframework.security:spring-security-oauth2-authorization-server:1.1.1'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'de.jottyfan:COJooq:2023.03' implementation 'de.jottyfan:COJooq:2023.03'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.0.0' implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.0.0'
implementation 'commons-io:commons-io:2.13.0'
runtimeOnly 'org.springframework.boot:spring-boot-starter-tomcat' runtimeOnly 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test' testImplementation 'org.springframework.security:spring-security-test'
} }
dependencyManagement { test {
imports { useJUnitPlatform()
mavenBom "org.keycloak.bom:keycloak-adapter-bom:${keycloakVersion}" }
}
// add version to manifest
springBoot {
buildInfo()
} }

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

57
gradlew vendored
View File

@ -1,21 +1,5 @@
#!/usr/bin/env sh #!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
############################################################################## ##############################################################################
## ##
## Gradle start up script for UN*X ## Gradle start up script for UN*X
@ -44,7 +28,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD="maximum"
@ -72,7 +56,7 @@ case "`uname`" in
Darwin* ) Darwin* )
darwin=true darwin=true
;; ;;
MSYS* | MINGW* ) MINGW* )
msys=true msys=true
;; ;;
NONSTOP* ) NONSTOP* )
@ -82,7 +66,6 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@ -126,11 +109,10 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi fi
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath # We build the pattern for arguments to be converted via cygpath
@ -156,19 +138,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
else else
eval `echo args$i`="\"$arg\"" eval `echo args$i`="\"$arg\""
fi fi
i=`expr $i + 1` i=$((i+1))
done done
case $i in case $i in
0) set -- ;; (0) set -- ;;
1) set -- "$args0" ;; (1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;; (2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;; (3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
@ -177,9 +159,14 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " " echo " "
} }
APP_ARGS=`save "$@"` APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules # Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" eval set -- $DEFAULT_JVM_OPTS --illegal-access=permit $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

View File

@ -0,0 +1,78 @@
package de.jottyfan.camporganizer.config;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
/**
*
* @author jotty
*
*/
@Configuration
public class AuthorizationConfiguration {
private static final String REALM_ACCESS_CLAIM = "realm_access";
private static final String ROLES_CLAIM = "roles";
private static final String RESOURCE_ACCESS_CLAIM = "resource_access";
@Value("${spring.security.oauth2.client.registration.keycloak.client-id}")
private String clientId;
@Bean
GrantedAuthoritiesMapper userAuthoritiesMapperForKeycloak() {
return authorities -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
var authority = authorities.iterator().next();
boolean isOidc = authority instanceof OidcUserAuthority;
if (isOidc) {
var oidcUserAuthority = (OidcUserAuthority) authority;
var userInfo = oidcUserAuthority.getUserInfo();
if (userInfo.hasClaim(REALM_ACCESS_CLAIM)) {
var realmAccess = userInfo.getClaimAsMap(REALM_ACCESS_CLAIM);
@SuppressWarnings("unchecked")
var roles = (Collection<String>) realmAccess.get(ROLES_CLAIM);
mappedAuthorities.addAll(generateAuthoritiesFromClaim(roles));
}
if (userInfo.hasClaim(RESOURCE_ACCESS_CLAIM)) {
var resourceAccess = userInfo.getClaimAsMap(RESOURCE_ACCESS_CLAIM);
if (resourceAccess.containsKey(clientId)) {
@SuppressWarnings("unchecked")
var roles = (Collection<String>) ((Map<?, ?>) resourceAccess.get(clientId)).get(ROLES_CLAIM);
mappedAuthorities.addAll(generateAuthoritiesFromClaim(roles));
}
}
} else {
var oauth2UserAuthority = (OAuth2UserAuthority) authority;
Map<String, Object> userAttributes = oauth2UserAuthority.getAttributes();
if (userAttributes.containsKey(REALM_ACCESS_CLAIM)) {
@SuppressWarnings("unchecked")
var realmAccess = (Map<String, Object>) userAttributes.get(REALM_ACCESS_CLAIM);
@SuppressWarnings("unchecked")
var roles = (Collection<String>) realmAccess.get(ROLES_CLAIM);
mappedAuthorities.addAll(generateAuthoritiesFromClaim(roles));
}
}
return mappedAuthorities;
};
}
private Collection<GrantedAuthority> generateAuthoritiesFromClaim(Collection<String> roles) {
return roles.stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role)).collect(Collectors.toList());
}
}

View File

@ -1,66 +0,0 @@
package de.jottyfan.camporganizer.config;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.keycloak.adapters.springsecurity.management.HttpSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
/**
*
* @author jotty
*
*/
@KeycloakConfiguration
@ComponentScan(basePackageClasses = KeycloakSpringBootConfigResolver.class)
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
@Bean
@Override
@ConditionalOnMissingBean(HttpSessionManager.class)
protected HttpSessionManager httpSessionManager() {
return new HttpSessionManager();
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
// @formatter:off
.antMatchers("/dashboard/**", "/business/**", "/confirmation/**").authenticated()
.anyRequest().permitAll();
// @formatter:on
// http.anonymous().disable();
http.csrf().disable();
}
}

View File

@ -0,0 +1,44 @@
package de.jottyfan.camporganizer.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
/**
*
* @author jotty
*
*/
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfiguration {
@Bean
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity sec, InMemoryClientRegistrationRepository crr)
throws Exception {
sec.csrf(o -> o.disable()).anonymous(o -> o.disable())
// @formatter:off
.oauth2Login(o -> o.defaultSuccessUrl("/"))
.logout(o -> o.logoutSuccessHandler(new OidcClientInitiatedLogoutSuccessHandler(crr)))
.authorizeHttpRequests(o -> o.requestMatchers("/dashboard/**", "/business/**", "/confirmation/**", "/userlogin/**").authenticated()
.anyRequest().permitAll())
.oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults()))
.sessionManagement(o -> o.init(sec));
// @formatter:on
return sec.build();
}
}

View File

@ -1,7 +1,6 @@
package de.jottyfan.camporganizer.module.admin; package de.jottyfan.camporganizer.module.admin;
import javax.servlet.http.HttpServletRequest; import java.security.Principal;
import javax.validation.Valid;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -17,8 +16,13 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes; import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import de.jottyfan.camporganizer.module.admin.model.CampBean;
import de.jottyfan.camporganizer.module.admin.model.DocumentBean;
import de.jottyfan.camporganizer.module.admin.model.LocationBean;
import de.jottyfan.camporganizer.module.camplist.CommonController; import de.jottyfan.camporganizer.module.camplist.CommonController;
import de.jottyfan.camporganizer.module.mail.MailBean; import de.jottyfan.camporganizer.module.mail.MailBean;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
/** /**
* *
@ -37,10 +41,10 @@ public class AdminController extends CommonController {
private String from; private String from;
@GetMapping("/admin/mail") @GetMapping("/admin/mail")
public String getMail(Model model, HttpServletRequest request) { public String getMail(Model model, Principal principal) {
MailBean mailBean = new MailBean(); MailBean mailBean = new MailBean();
mailBean.setFrom(from); mailBean.setFrom(from);
mailBean.getTo().add(getCurrentEmail(request)); mailBean.getTo().add(getCurrentEmail(principal));
model.addAttribute("bean", mailBean); model.addAttribute("bean", mailBean);
return "/admin/mail"; return "/admin/mail";
} }

View File

@ -0,0 +1,125 @@
package de.jottyfan.camporganizer.module.admin;
import java.util.List;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import de.jottyfan.camporganizer.db.jooq.enums.EnumModule;
import de.jottyfan.camporganizer.module.admin.model.CampBean;
import de.jottyfan.camporganizer.module.admin.model.CampProfileBean;
import de.jottyfan.camporganizer.module.admin.model.ProfileBean;
import de.jottyfan.camporganizer.module.camplist.CommonController;
/**
*
* @author jotty
*
*/
@Controller
public class AdminPrivilegesController extends CommonController {
@Autowired
private AdminPrivilegesService service;
@GetMapping("/admin/privileges/userbased")
public String getUserbased(Model model) {
model.addAttribute("list", service.getProfiles());
return "/admin/privileges/userbased";
}
@GetMapping("/admin/privileges/rolebased")
public String getRolebased(Model model) {
model.addAttribute("list", service.getAllModules());
return "/admin/privileges/rolebased";
}
@GetMapping("/admin/privileges/campbased")
public String getCampbased(Model model) {
model.addAttribute("list", service.getAllCamps());
return "/admin/privileges/campbased";
}
@GetMapping("/admin/privileges/rolebased/{role}")
public String getRolebased(@PathVariable String role, Model model) {
model.addAttribute("list", service.getAllModules());
model.addAttribute("selected", role);
model.addAttribute("container", service.getPersonCampMappingByModule(EnumModule.valueOf(role)));
model.addAttribute("pagedest", "_admin_privileges_rolebased_" + role);
return "/admin/privileges/rolebased";
}
@GetMapping("/admin/privileges/campbased/{campid}")
public String getCampbased(@PathVariable Integer campid, Model model) {
List<CampBean> list = service.getAllCamps();
model.addAttribute("list", list);
String campname = "?";
for (CampBean p : list) {
if (p.getPk().equals(campid)) {
campname = p.getFullname();
}
}
model.addAttribute("selected", campname);
model.addAttribute("container", service.getPersonModuleMappingByCamp(campid));
model.addAttribute("pagedest", "_admin_privileges_campbased_" + campid);
return "/admin/privileges/campbased";
}
@GetMapping("/admin/privileges/userbased/{userid}")
public String getUserbased(@PathVariable Integer userid, Model model) {
List<ProfileBean> list = service.getProfiles();
model.addAttribute("list", list);
String selected = "?";
for (ProfileBean p : list) {
if (p.getPk().equals(userid)) {
selected = p.getFullname();
}
}
model.addAttribute("selected", selected);
model.addAttribute("container", service.getModuleCampMappingByPerson(userid));
model.addAttribute("pagedest", "_admin_privileges_userbased_" + userid);
return "/admin/privileges/userbased";
}
@GetMapping("/admin/privileges/delete/{id}/{pagedest}")
public String deleteFromCampProfile(@PathVariable Integer id, @PathVariable String pagedest) {
service.deleteFromCampProfile(id);
return "redirect:" + pagedest.replace("_", "/");
}
@GetMapping("/admin/privileges/add/{pagedest}")
public String prepareAdd(Model model, @PathVariable String pagedest) {
model.addAttribute("pagedest", pagedest);
model.addAttribute("bean", new CampProfileBean());
model.addAttribute("profiles", service.getProfiles());
model.addAttribute("camps", service.getAllCamps());
model.addAttribute("modules", service.getAllModules());
return "/admin/privileges/add";
}
@PostMapping("/admin/privileges/insert/{pagedest}")
public String insertCampProfile(@Valid @ModelAttribute("bean") CampProfileBean bean, final BindingResult bindingResult, @PathVariable String pagedest, Model model) {
if (bindingResult.hasErrors()) {
model.addAttribute("pagedest", pagedest);
model.addAttribute("profiles", service.getProfiles());
model.addAttribute("camps", service.getAllCamps());
model.addAttribute("modules", service.getAllModules());
return "/admin/privileges/add";
}
service.insertIntoCampProfile(bean);
return "redirect:" + pagedest.replace("_", "/");
}
@GetMapping("/admin/privileges/abortinsert/{pagedest}")
public String abortInsert(@PathVariable String pagedest) {
return "redirect:" + pagedest.replace("_", "/");
}
}

View File

@ -0,0 +1,100 @@
package de.jottyfan.camporganizer.module.admin;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.db.jooq.enums.EnumModule;
import de.jottyfan.camporganizer.module.admin.model.CampBean;
import de.jottyfan.camporganizer.module.admin.model.CampProfileBean;
import de.jottyfan.camporganizer.module.admin.model.PrivilegesContainerBean;
import de.jottyfan.camporganizer.module.admin.model.ProfileBean;
/**
*
* @author jotty
*
*/
@Service
public class AdminPrivilegesService {
@Autowired
private AdminRepository adminRepository;
/**
* get all camp beans from the database
*
* @return all camp beans; an empty list at least
*/
public List<CampBean> getAllCamps() {
return adminRepository.getAllCamps();
}
/**
* get all profiles from the db
*
* @return the profiles
*/
public List<ProfileBean> getProfiles() {
return adminRepository.getProfiles();
}
/**
* get all roles
*
* @return all roles
*/
public List<String> getAllModules() {
return adminRepository.getAllModules();
}
/**
* get the mapping that contains the form bean for a selected module
*
* @param module the module
* @return the container
*/
public PrivilegesContainerBean getPersonCampMappingByModule(EnumModule module) {
return adminRepository.getPersonCampMappingByModule(module);
}
/**
* get the mapping that contains the form bean for a selected camp
*
* @param camp the camp ID
* @return the container
*/
public PrivilegesContainerBean getPersonModuleMappingByCamp(Integer camp) {
return adminRepository.getPersonModuleMappingByCamp(camp);
}
/**
* get the mapping that contains the form bean for a selected user
*
* @param user the user ID
* @return the container
*/
public PrivilegesContainerBean getModuleCampMappingByPerson(Integer user) {
return adminRepository.getModuleCampMappingByPerson(user);
}
/**
* delete from camp profile
*
* @param id the ID
*/
public void deleteFromCampProfile(Integer id) {
adminRepository.deleteFromCampProfile(id);
}
/**
* add the camp profile to the database
*
* @param bean the bean
*/
public void insertIntoCampProfile(CampProfileBean bean) {
EnumModule m = bean.getModule() == null ? null : EnumModule.valueOf(bean.getModule());
adminRepository.addToCampProfile(bean.getFkCamp(), bean.getFkProfile(), m);
}
}

View File

@ -1,6 +1,7 @@
package de.jottyfan.camporganizer.module.admin; package de.jottyfan.camporganizer.module.admin;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP; import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPPROFILE;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENT; import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENT;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENTROLE; import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENTROLE;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_LOCATION; import static de.jottyfan.camporganizer.db.jooq.Tables.T_LOCATION;
@ -15,7 +16,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.validation.Valid; import jakarta.validation.Valid;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -31,6 +32,7 @@ import org.jooq.Record4;
import org.jooq.Record5; import org.jooq.Record5;
import org.jooq.SelectConditionStep; import org.jooq.SelectConditionStep;
import org.jooq.SelectSeekStep1; import org.jooq.SelectSeekStep1;
import org.jooq.SelectSeekStep2;
import org.jooq.SelectWhereStep; import org.jooq.SelectWhereStep;
import org.jooq.UpdateConditionStep; import org.jooq.UpdateConditionStep;
import org.jooq.UpdateSetMoreStep; import org.jooq.UpdateSetMoreStep;
@ -43,13 +45,21 @@ import org.springframework.transaction.annotation.Transactional;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole; import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumDocument; import de.jottyfan.camporganizer.db.jooq.enums.EnumDocument;
import de.jottyfan.camporganizer.db.jooq.enums.EnumFiletype; import de.jottyfan.camporganizer.db.jooq.enums.EnumFiletype;
import de.jottyfan.camporganizer.db.jooq.enums.EnumModule;
import de.jottyfan.camporganizer.db.jooq.tables.records.TCampRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TCampRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TCampprofileRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TDocumentRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TDocumentRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TDocumentroleRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TDocumentroleRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TLocationRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TLocationRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TProfileRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TProfileRecord;
import de.jottyfan.camporganizer.module.camplist.LambdaResultWrapper; import de.jottyfan.camporganizer.module.admin.model.CampBean;
import de.jottyfan.camporganizer.module.admin.model.DocumentBean;
import de.jottyfan.camporganizer.module.admin.model.IntKeyValueBean;
import de.jottyfan.camporganizer.module.admin.model.LocationBean;
import de.jottyfan.camporganizer.module.admin.model.PrivilegesContainerBean;
import de.jottyfan.camporganizer.module.admin.model.ProfileBean;
import de.jottyfan.camporganizer.module.camplist.model.LambdaResultWrapper;
/** /**
* *
@ -482,4 +492,160 @@ public class AdminRepository {
} }
return list; return list;
} }
/**
* get all modules
*
* @return all modules
*/
public List<String> getAllModules() {
List<String> list = new ArrayList<>();
for (EnumModule r : EnumModule.values()) {
list.add(r.getLiteral());
}
return list;
}
/**
* get the mapping that contains the form bean for a selected module
*
* @param module the module
* @return the container
*/
public PrivilegesContainerBean getPersonCampMappingByModule(EnumModule module) {
SelectSeekStep2<Record4<Integer, String, LocalDateTime, String>, LocalDateTime, Integer> sql = jooq
// @formatter:off
.select(T_CAMPPROFILE.PK,
T_CAMP.NAME,
T_CAMP.ARRIVE,
T_PROFILE.FORENAME.concat(" ").concat(T_PROFILE.SURNAME).as(T_PROFILE.USERNAME))
.from(T_CAMPPROFILE)
.leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_CAMPPROFILE.FK_CAMP))
.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE))
.where(T_CAMPPROFILE.MODULE.eq(module))
.orderBy(T_CAMP.ARRIVE, T_PROFILE.PK);
// @formatter:on
LOGGER.debug(sql.toString());
PrivilegesContainerBean pcb = new PrivilegesContainerBean();
for (Record4<Integer, String, LocalDateTime, String> r : sql.fetch()) {
Integer fkCampProfile = r.get(T_CAMPPROFILE.PK);
String username = r.get(T_PROFILE.USERNAME);
String campname = r.get(T_CAMP.NAME);
LocalDateTime ldt = r.get(T_CAMP.ARRIVE);
String year = ldt != null ? String.valueOf(ldt.getYear()) : "?";
campname = campname == null ? year : campname.concat(" ").concat(year);
if (pcb.get(username) == null) {
pcb.addKey(username);
}
pcb.add(username, IntKeyValueBean.of(fkCampProfile, campname));
}
return pcb;
}
/**
* get the mapping that contains the form bean for a selected user
*
* @param user the user ID
* @return the container
*/
public PrivilegesContainerBean getModuleCampMappingByPerson(Integer user) {
SelectSeekStep1<Record4<Integer, String, LocalDateTime, EnumModule>, LocalDateTime> sql = jooq
// @formatter:off
.select(T_CAMPPROFILE.PK,
T_CAMP.NAME,
T_CAMP.ARRIVE,
T_CAMPPROFILE.MODULE)
.from(T_CAMPPROFILE)
.leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_CAMPPROFILE.FK_CAMP))
.where(T_CAMPPROFILE.FK_PROFILE.eq(user))
.orderBy(T_CAMP.ARRIVE);
// @formatter:on
LOGGER.debug(sql.toString());
PrivilegesContainerBean pcb = new PrivilegesContainerBean();
for (Record4<Integer, String, LocalDateTime, EnumModule> r : sql.fetch()) {
Integer fkCampProfile = r.get(T_CAMPPROFILE.PK);
EnumModule moduleEnum = r.get(T_CAMPPROFILE.MODULE);
String module = moduleEnum == null ? null : moduleEnum.getLiteral();
String campname = r.get(T_CAMP.NAME);
LocalDateTime ldt = r.get(T_CAMP.ARRIVE);
String year = ldt != null ? String.valueOf(ldt.getYear()) : "?";
campname = campname == null ? year : campname.concat(" ").concat(year);
if (pcb.get(module) == null) {
pcb.addKey(module);
}
pcb.add(module, IntKeyValueBean.of(fkCampProfile, campname));
}
return pcb;
}
/**
* get the mapping that contains the form bean for a selected camp
*
* @param camp the camp ID
* @return the container
*/
public PrivilegesContainerBean getPersonModuleMappingByCamp(Integer camp) {
SelectSeekStep2<Record4<Integer, String, String, EnumModule>, String, String> sql = jooq
// @formatter:off
.select(T_CAMPPROFILE.PK,
T_PROFILE.FORENAME,
T_PROFILE.SURNAME,
T_CAMPPROFILE.MODULE)
.from(T_CAMPPROFILE)
.leftJoin(T_CAMP).on(T_CAMP.PK.eq(T_CAMPPROFILE.FK_CAMP))
.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE))
.where(T_CAMPPROFILE.FK_CAMP.eq(camp))
.orderBy(T_PROFILE.SURNAME, T_PROFILE.FORENAME);
// @formatter:on
LOGGER.debug(sql.toString());
PrivilegesContainerBean pcb = new PrivilegesContainerBean();
for (Record4<Integer, String, String, EnumModule> r : sql.fetch()) {
Integer fkCampProfile = r.get(T_CAMPPROFILE.PK);
EnumModule moduleEnum = r.get(T_CAMPPROFILE.MODULE);
String forename = r.get(T_PROFILE.FORENAME);
String surname = r.get(T_PROFILE.SURNAME);
String person = new StringBuilder().append(forename).append(" ").append(surname).toString();
String module = moduleEnum == null ? null : moduleEnum.getLiteral();
if (pcb.get(person) == null) {
pcb.addKey(person);
}
pcb.add(person, IntKeyValueBean.of(fkCampProfile, module));
}
return pcb;
}
/**
* delete entry from camp profile
*
* @param id the pk
*/
public void deleteFromCampProfile(Integer id) {
DeleteConditionStep<TCampprofileRecord> sql = jooq.deleteFrom(T_CAMPPROFILE).where(T_CAMPPROFILE.PK.eq(id));
LOGGER.debug(sql.toString());
sql.execute();
}
/**
* add entry to database
*
* @param fkCamp the camp ID
* @param fkProfile the profile ID
* @param module the module
*/
public void addToCampProfile(Integer fkCamp, Integer fkProfile, EnumModule module) {
InsertReturningStep<TCampprofileRecord> sql = jooq
// @formatter:off
.insertInto(T_CAMPPROFILE,
T_CAMPPROFILE.FK_CAMP,
T_CAMPPROFILE.FK_PROFILE,
T_CAMPPROFILE.MODULE)
.values(fkCamp, fkProfile, module)
.onConflict(T_CAMPPROFILE.FK_CAMP,
T_CAMPPROFILE.FK_PROFILE,
T_CAMPPROFILE.MODULE)
.doNothing();
// @formatter:on
LOGGER.debug(sql.toString());
sql.execute();
}
} }

View File

@ -5,7 +5,7 @@ import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENT;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import javax.validation.Valid; import jakarta.validation.Valid;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -13,6 +13,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.db.jooq.enums.EnumDocument; import de.jottyfan.camporganizer.db.jooq.enums.EnumDocument;
import de.jottyfan.camporganizer.module.admin.model.CampBean;
import de.jottyfan.camporganizer.module.admin.model.DocumentBean;
import de.jottyfan.camporganizer.module.admin.model.LocationBean;
import de.jottyfan.camporganizer.module.admin.model.ProfileBean;
import de.jottyfan.camporganizer.module.mail.MailBean; import de.jottyfan.camporganizer.module.mail.MailBean;
import de.jottyfan.camporganizer.module.mail.MailRepository; import de.jottyfan.camporganizer.module.mail.MailRepository;

View File

@ -1,13 +1,14 @@
package de.jottyfan.camporganizer.module.admin; package de.jottyfan.camporganizer.module.admin.model;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
@ -73,6 +74,10 @@ public class CampBean implements Serializable {
return bean; return bean;
} }
public String getFullname() {
return new StringBuilder().append(name).append(" ").append(arrive == null ? "?" : arrive.format(DateTimeFormatter.ofPattern("yyyy"))).toString();
}
/** /**
* @return the pk * @return the pk
*/ */

View File

@ -0,0 +1,80 @@
package de.jottyfan.camporganizer.module.admin.model;
import java.io.Serializable;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
*
* @author jotty
*
*/
public class CampProfileBean implements Serializable {
private static final long serialVersionUID = 1L;
private Integer pk;
@NotNull
private Integer fkCamp;
@NotNull
private Integer fkProfile;
@NotBlank
private String module;
/**
* @return the pk
*/
public Integer getPk() {
return pk;
}
/**
* @param pk the pk to set
*/
public void setPk(Integer pk) {
this.pk = pk;
}
/**
* @return the fkCamp
*/
public Integer getFkCamp() {
return fkCamp;
}
/**
* @param fkCamp the fkCamp to set
*/
public void setFkCamp(Integer fkCamp) {
this.fkCamp = fkCamp;
}
/**
* @return the fkProfile
*/
public Integer getFkProfile() {
return fkProfile;
}
/**
* @param fkProfile the fkProfile to set
*/
public void setFkProfile(Integer fkProfile) {
this.fkProfile = fkProfile;
}
/**
* @return the module
*/
public String getModule() {
return module;
}
/**
* @param module the module to set
*/
public void setModule(String module) {
this.module = module;
}
}

View File

@ -1,12 +1,12 @@
package de.jottyfan.camporganizer.module.admin; package de.jottyfan.camporganizer.module.admin.model;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.Serializable; import java.io.Serializable;
import java.util.Base64; import java.util.Base64;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;

View File

@ -0,0 +1,50 @@
package de.jottyfan.camporganizer.module.admin.model;
import java.io.Serializable;
/**
*
* @author jotty
*
*/
public class IntKeyValueBean implements Serializable {
private static final long serialVersionUID = 1L;
private Integer key;
private String value;
public static final IntKeyValueBean of(Integer key, String value) {
IntKeyValueBean bean = new IntKeyValueBean();
bean.setKey(key);
bean.setValue(value);
return bean;
}
/**
* @return the key
*/
public Integer getKey() {
return key;
}
/**
* @param key the key to set
*/
public void setKey(Integer key) {
this.key = key;
}
/**
* @return the value
*/
public String getValue() {
return value;
}
/**
* @param value the value to set
*/
public void setValue(String value) {
this.value = value;
}
}

View File

@ -1,9 +1,9 @@
package de.jottyfan.camporganizer.module.admin; package de.jottyfan.camporganizer.module.admin.model;
import java.io.Serializable; import java.io.Serializable;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import de.jottyfan.camporganizer.db.jooq.tables.records.TLocationRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TLocationRecord;

View File

@ -0,0 +1,62 @@
package de.jottyfan.camporganizer.module.admin.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* @author jotty
*
*/
public class PrivilegesContainerBean implements Serializable {
private static final long serialVersionUID = 1L;
private Map<String, List<IntKeyValueBean>> map;
public PrivilegesContainerBean() {
map = new HashMap<>();
}
/**
* get the content of key
*
* @param key the key
* @return the value
*/
public List<IntKeyValueBean> get(String key) {
return map == null ? null : map.get(key);
}
/**
* add the value to the key
*
* @param key the key
* @param value the value
*/
public void add(String key, IntKeyValueBean value) {
if (get(key) != null) {
map.get(key).add(value);
} else {
throw new NullPointerException("key not found");
}
}
/**
* add the key to this map
*
* @param key
*/
public void addKey(String key) {
map.put(key, new ArrayList<>());
}
/**
* @return the map
*/
public Map<String, List<IntKeyValueBean>> getMap() {
return map;
}
}

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.admin; package de.jottyfan.camporganizer.module.admin.model;
import java.io.Serializable; import java.io.Serializable;
@ -27,6 +27,10 @@ public class ProfileBean implements Serializable {
return bean; return bean;
} }
public String getFullname() {
return new StringBuilder().append(forename).append(" ").append(surname).toString();
}
/** /**
* @return the pk * @return the pk
*/ */

View File

@ -1,6 +1,6 @@
package de.jottyfan.camporganizer.module.business.bookings; package de.jottyfan.camporganizer.module.business.bookings;
import javax.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -13,8 +13,8 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import de.jottyfan.camporganizer.module.business.bookings.impl.AddPaymentBean; import de.jottyfan.camporganizer.module.business.bookings.model.AddPaymentBean;
import de.jottyfan.camporganizer.module.business.bookings.impl.BookerBean; import de.jottyfan.camporganizer.module.business.bookings.model.BookerBean;
import de.jottyfan.camporganizer.module.camplist.CommonController; import de.jottyfan.camporganizer.module.camplist.CommonController;
/** /**
@ -27,7 +27,7 @@ public class BookingsController extends CommonController {
private static final Logger LOGGER = LogManager.getLogger(BookingsController.class); private static final Logger LOGGER = LogManager.getLogger(BookingsController.class);
@Autowired @Autowired
private IBookingsService bookingsService; private BookingsService bookingsService;
@GetMapping("/business/bookings") @GetMapping("/business/bookings")
@RolesAllowed({"business_booking"}) @RolesAllowed({"business_booking"})

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.bookings.impl; package de.jottyfan.camporganizer.module.business.bookings;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSON; 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_PROFILE;
@ -27,6 +27,7 @@ import de.jottyfan.camporganizer.db.EnumConverter;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole; import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumSex; import de.jottyfan.camporganizer.db.jooq.enums.EnumSex;
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord;
import de.jottyfan.camporganizer.module.business.bookings.model.BookerBean;
/** /**
* *

View File

@ -1,11 +1,11 @@
package de.jottyfan.camporganizer.module.business.bookings.impl; package de.jottyfan.camporganizer.module.business.bookings;
import java.util.List; import java.util.List;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.module.business.bookings.IBookingsService; import de.jottyfan.camporganizer.module.business.bookings.model.BookerBean;
/** /**
* *
@ -13,24 +13,20 @@ import de.jottyfan.camporganizer.module.business.bookings.IBookingsService;
* *
*/ */
@Service @Service
public class BookingsService implements IBookingsService { public class BookingsService {
@Autowired @Autowired
private BookingsRepository bookingsGateway; private BookingsRepository bookingsGateway;
@Override
public List<BookerBean> getBookers(String username) { public List<BookerBean> getBookers(String username) {
return bookingsGateway.getBookings(username); return bookingsGateway.getBookings(username);
} }
@Override
public BookerBean getBooker(Integer id, String username) { public BookerBean getBooker(Integer id, String username) {
return bookingsGateway.getBooking(id, username); return bookingsGateway.getBooking(id, username);
} }
@Override
public Integer addPayment(Integer id, Double payment) { public Integer addPayment(Integer id, Double payment) {
return bookingsGateway.addPayment(id, payment); return bookingsGateway.addPayment(id, payment);
} }
} }

View File

@ -1,38 +0,0 @@
package de.jottyfan.camporganizer.module.business.bookings;
import java.util.List;
import de.jottyfan.camporganizer.module.business.bookings.impl.BookerBean;
/**
*
* @author jotty
*
*/
public interface IBookingsService {
/**
* get the bookers information
*
* @param username the name of the user in this session
* @return the list of bookers; an empty one at least
*/
public List<BookerBean> getBookers(String username);
/**
* get the booker referenced by id
*
* @param id the ID of the booker
* @param username the name of the user in this session
* @return the booker if found; null otherwise
*/
public BookerBean getBooker(Integer id, String username);
/**
* add payment to the paid values of user with id
*
* @param id the ID of the booker
* @param payment the payment (additional value)
* @return number of affected database rows, should be 1
*/
public Integer addPayment(Integer id, Double payment);
}

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.bookings.impl; package de.jottyfan.camporganizer.module.business.bookings.model;
import java.io.Serializable; import java.io.Serializable;

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.bookings.impl; package de.jottyfan.camporganizer.module.business.bookings.model;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;

View File

@ -1,7 +1,5 @@
package de.jottyfan.camporganizer.module.business.business; package de.jottyfan.camporganizer.module.business.business;
import javax.servlet.http.HttpServletRequest;
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.ui.Model;
@ -18,15 +16,12 @@ import de.jottyfan.camporganizer.module.camplist.CommonController;
public class BusinessController extends CommonController { public class BusinessController extends CommonController {
@Autowired @Autowired
private HttpServletRequest request; private BusinessService service;
@Autowired
private IBusinessService indexService;
@GetMapping("/business") @GetMapping("/business")
public String getIndex(Model model) { public String getIndex(Model model) {
String username = indexService.getCurrentUser(request); String username = super.getCurrentUser();
model.addAttribute("campBudgets", indexService.getCampBudgets(username)); model.addAttribute("campBudgets", service.getCampBudgets(username));
return "business/business"; return "business/business";
} }
} }

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.business.impl; package de.jottyfan.camporganizer.module.business.business;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP; import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_LOCATION; import static de.jottyfan.camporganizer.db.jooq.Tables.T_LOCATION;
@ -25,6 +25,8 @@ import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord;
import de.jottyfan.camporganizer.module.business.business.model.BusinessBean;
import de.jottyfan.camporganizer.module.business.business.model.CampBudgetBean;
/** /**
* *

View File

@ -0,0 +1,24 @@
package de.jottyfan.camporganizer.module.business.business;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.module.business.business.model.CampBudgetBean;
/**
*
* @author jotty
*
*/
@Service
public class BusinessService {
@Autowired
private BusinessRepository gateway;
public List<CampBudgetBean> getCampBudgets(String username) {
return gateway.getCampBudgets(username);
}
}

View File

@ -1,30 +0,0 @@
package de.jottyfan.camporganizer.module.business.business;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import de.jottyfan.camporganizer.module.business.business.impl.CampBudgetBean;
/**
*
* @author jotty
*
*/
public interface IBusinessService {
/**
* get the user of this session
*
* @param request the request
* @return the username of the current user
*/
public String getCurrentUser(HttpServletRequest request);
/**
* get a list of all camp budgets of all years
*
* @param username the name of the current user in this session
* @return the list; an empty one at least
*/
public List<CampBudgetBean> getCampBudgets(String username);
}

View File

@ -1,38 +0,0 @@
package de.jottyfan.camporganizer.module.business.business.impl;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.jooq.exception.DataAccessException;
import org.keycloak.KeycloakSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.module.business.business.IBusinessService;
/**
*
* @author jotty
*
*/
@Service
public class BusinessService implements IBusinessService {
@Autowired
private BusinessRepository gateway;
@Override
public String getCurrentUser(HttpServletRequest request) {
KeycloakSecurityContext ksc = (KeycloakSecurityContext) request
.getAttribute(KeycloakSecurityContext.class.getName());
if (ksc == null) {
throw new DataAccessException("no keycloak user in session");
}
return ksc.getIdToken().getPreferredUsername();
}
@Override
public List<CampBudgetBean> getCampBudgets(String username) {
return gateway.getCampBudgets(username);
}
}

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.business.impl; package de.jottyfan.camporganizer.module.business.business.model;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.business.impl; package de.jottyfan.camporganizer.module.business.business.model;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;

View File

@ -1,38 +1,29 @@
package de.jottyfan.camporganizer.module.business.camp; package de.jottyfan.camporganizer.module.business.camp;
import javax.annotation.security.RolesAllowed;
import javax.servlet.http.HttpServletRequest;
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.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import de.jottyfan.camporganizer.module.business.business.IBusinessService;
import de.jottyfan.camporganizer.module.camplist.CommonController; import de.jottyfan.camporganizer.module.camplist.CommonController;
import jakarta.annotation.security.RolesAllowed;
/** /**
* *
* @author jotty * @author jotty
* *
*/ */
@Controller @Controller
public class CampController extends CommonController { public class CampController extends CommonController {
@Autowired @Autowired
private HttpServletRequest request; private CampService campService;
@Autowired
private IBusinessService indexService;
@Autowired
private ICampService campService;
@GetMapping("/business/camp/{id}") @GetMapping("/business/camp/{id}")
@RolesAllowed({"business"}) @RolesAllowed({ "business" })
public String getCamp(Model model, @PathVariable Integer id) { public String getCamp(Model model, @PathVariable Integer id) {
String username = indexService.getCurrentUser(request); String username = super.getCurrentUser();
model.addAttribute("currentUser", username); model.addAttribute("currentUser", username);
model.addAttribute("campId", id); model.addAttribute("campId", id);
model.addAttribute("camp", campService.getCamp(id, username)); model.addAttribute("camp", campService.getCamp(id, username));

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.camp.impl; package de.jottyfan.camporganizer.module.business.camp;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSON; 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_PROFILE;
@ -25,6 +25,8 @@ import org.springframework.transaction.annotation.Transactional;
import de.jottyfan.camporganizer.db.EnumConverter; import de.jottyfan.camporganizer.db.EnumConverter;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole; import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumSex; import de.jottyfan.camporganizer.db.jooq.enums.EnumSex;
import de.jottyfan.camporganizer.module.business.camp.model.CampBean;
import de.jottyfan.camporganizer.module.business.camp.model.PersonBean;
/** /**
* *

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.camp.impl; package de.jottyfan.camporganizer.module.business.camp;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
@ -6,7 +6,9 @@ import java.util.List;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.module.business.camp.ICampService; import de.jottyfan.camporganizer.module.business.camp.model.BookingBean;
import de.jottyfan.camporganizer.module.business.camp.model.CampBean;
import de.jottyfan.camporganizer.module.business.camp.model.PersonBean;
/** /**
* *
@ -14,17 +16,15 @@ import de.jottyfan.camporganizer.module.business.camp.ICampService;
* *
*/ */
@Service @Service
public class CampService implements ICampService { public class CampService {
@Autowired @Autowired
private CampRepository campGateway; private CampRepository campGateway;
@Override
public CampBean getCamp(Integer id, String username) { public CampBean getCamp(Integer id, String username) {
return campGateway.getCamp(id, username); return campGateway.getCamp(id, username);
} }
@Override
public BookingBean getBookings(Integer id, String username) { public BookingBean getBookings(Integer id, String username) {
Integer approved = 0; Integer approved = 0;
Integer open = 0; Integer open = 0;
@ -35,9 +35,9 @@ public class CampService implements ICampService {
if (acceptence == null) { if (acceptence == null) {
open += 1; open += 1;
} else if (acceptence) { } else if (acceptence) {
approved +=1; approved += 1;
} else { } else {
rejected +=1; rejected += 1;
} }
paid = paid.add(p.getPaid() == null ? new BigDecimal(0) : p.getPaid()); paid = paid.add(p.getPaid() == null ? new BigDecimal(0) : p.getPaid());
} }
@ -49,9 +49,7 @@ public class CampService implements ICampService {
return bean; return bean;
} }
@Override
public List<PersonBean> getBookers(Integer id, String username) { public List<PersonBean> getBookers(Integer id, String username) {
return campGateway.getBookings(id, username); return campGateway.getBookings(id, username);
} }
} }

View File

@ -1,43 +0,0 @@
package de.jottyfan.camporganizer.module.business.camp;
import java.util.List;
import de.jottyfan.camporganizer.module.business.camp.impl.BookingBean;
import de.jottyfan.camporganizer.module.business.camp.impl.CampBean;
import de.jottyfan.camporganizer.module.business.camp.impl.PersonBean;
/**
*
* @author jotty
*
*/
public interface ICampService {
/**
* get the camp bean of id
*
* @param id the id of the camp
* @param username the name of the user in this session
* @return the camp bean if found; null otherwise
*/
public CampBean getCamp(Integer id, String username);
/**
* get the booking information for camp with id
*
* @param id the id of the camp
* @param username the name of the user in this session
* @return a booking bean
*/
public BookingBean getBookings(Integer id, String username);
/**
* get the bookers information for camp with id
*
* @param id the id of the camp
* @param username the name of the user in this session
* @return the list of bookers; an empty one at least
*/
public List<PersonBean> getBookers(Integer id, String username);
}

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.camp.impl; package de.jottyfan.camporganizer.module.business.camp.model;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.camp.impl; package de.jottyfan.camporganizer.module.business.camp.model;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;

View File

@ -1,11 +1,11 @@
package de.jottyfan.camporganizer.module.business.camp.impl; package de.jottyfan.camporganizer.module.business.camp.model;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import de.jottyfan.camporganizer.module.business.privileges.impl.ProfileBean; import de.jottyfan.camporganizer.module.business.privileges.model.ProfileBean;
/** /**
* *

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.camp.impl; package de.jottyfan.camporganizer.module.business.camp.model;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;

View File

@ -1,9 +1,5 @@
package de.jottyfan.camporganizer.module.business.privileges; package de.jottyfan.camporganizer.module.business.privileges;
import javax.annotation.security.RolesAllowed;
import javax.servlet.http.HttpServletRequest;
import javax.websocket.server.PathParam;
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.ui.Model;
@ -12,10 +8,10 @@ import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole; import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.module.business.business.IBusinessService; import de.jottyfan.camporganizer.module.business.privileges.model.PrivilegesBean;
import de.jottyfan.camporganizer.module.business.privileges.impl.PrivilegesBean;
import de.jottyfan.camporganizer.module.business.privileges.impl.PrivilegesService;
import de.jottyfan.camporganizer.module.camplist.CommonController; import de.jottyfan.camporganizer.module.camplist.CommonController;
import jakarta.annotation.security.RolesAllowed;
import jakarta.websocket.server.PathParam;
/** /**
* *
@ -25,19 +21,13 @@ import de.jottyfan.camporganizer.module.camplist.CommonController;
@Controller @Controller
public class PrivilegesController extends CommonController { public class PrivilegesController extends CommonController {
@Autowired
private HttpServletRequest request;
@Autowired @Autowired
private PrivilegesService privilegesService; private PrivilegesService privilegesService;
@Autowired
private IBusinessService indexService;
@GetMapping("/business/privileges") @GetMapping("/business/privileges")
@RolesAllowed({ "admin" }) @RolesAllowed({ "admin" })
public String getIndex(Model model) { public String getIndex(Model model) {
String username = indexService.getCurrentUser(request); String username = super.getCurrentUser();
model.addAttribute("currentUser", username); model.addAttribute("currentUser", username);
model.addAttribute("privileges", privilegesService.getPrivileges()); model.addAttribute("privileges", privilegesService.getPrivileges());
model.addAttribute("profiles", privilegesService.getProfiles(EnumCamprole.director, EnumCamprole.teacher)); model.addAttribute("profiles", privilegesService.getProfiles(EnumCamprole.director, EnumCamprole.teacher));
@ -59,7 +49,7 @@ public class PrivilegesController extends CommonController {
PrivilegesBean bean = new PrivilegesBean(); PrivilegesBean bean = new PrivilegesBean();
bean.setFkCamp(fkCamp); bean.setFkCamp(fkCamp);
bean.setFkProfile(fkProfile); bean.setFkProfile(fkProfile);
privilegesService.remove(bean, indexService.getCurrentUser(request)); privilegesService.remove(bean, super.getCurrentUser());
return getIndex(model); return getIndex(model);
} }
} }

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.privileges.impl; package de.jottyfan.camporganizer.module.business.privileges;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSON; 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_PROFILE;
@ -24,6 +24,8 @@ import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import de.jottyfan.camporganizer.db.jooq.tables.records.TSalesprofileRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TSalesprofileRecord;
import de.jottyfan.camporganizer.module.business.privileges.model.PrivilegesBean;
import de.jottyfan.camporganizer.module.business.privileges.model.ProfileBean;
/** /**
* *

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.privileges.impl; package de.jottyfan.camporganizer.module.business.privileges;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -10,7 +10,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole; import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.module.business.camp.impl.CampBean; import de.jottyfan.camporganizer.module.business.camp.model.CampBean;
import de.jottyfan.camporganizer.module.business.privileges.model.PrivilegesBean;
import de.jottyfan.camporganizer.module.business.privileges.model.ProfileBean;
/** /**
* *

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.privileges.impl; package de.jottyfan.camporganizer.module.business.privileges.model;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.business.privileges.impl; package de.jottyfan.camporganizer.module.business.privileges.model;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;

View File

@ -1,7 +1,6 @@
package de.jottyfan.camporganizer.module.camplist; package de.jottyfan.camporganizer.module.camplist;
import javax.servlet.ServletException; import java.security.Principal;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -12,7 +11,9 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import de.jottyfan.camporganizer.module.registration.KeycloakRepository; import de.jottyfan.camporganizer.module.camplist.model.BookingBean;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
/** /**
* *
@ -23,26 +24,19 @@ import de.jottyfan.camporganizer.module.registration.KeycloakRepository;
public class CamplistController extends CommonController { public class CamplistController extends CommonController {
private static final Logger LOGGER = LogManager.getLogger(CamplistController.class); private static final Logger LOGGER = LogManager.getLogger(CamplistController.class);
@Autowired
private HttpServletRequest request;
@Autowired
private KeycloakRepository keycloak;
@Autowired @Autowired
private CamplistService service; private CamplistService service;
@GetMapping("/camplist") @GetMapping("/camplist")
public String index(Model model) { public String index(Model model, Principal principal) {
model.addAttribute("camps", service.getAllCamps(true)); model.addAttribute("camps", service.getAllCamps(true));
return super.isLoggedIn(request) ? dashboard(model) : "/camplist"; return super.isLoggedIn(principal) ? dashboard(model) : "/camplist";
} }
@GetMapping("/dashboard") @GetMapping("/dashboard")
public String dashboard(Model model) { public String dashboard(Model model) {
model.addAttribute("mybookings", service.getBookingsOf(super.getCurrentUser(request))); model.addAttribute("mybookings", service.getBookingsOf(super.getCurrentUser()));
model.addAttribute("bookingBean", new BookingBean()); model.addAttribute("bookingBean", new BookingBean());
model.addAttribute("keycloakProfileUrl", keycloak.getUserClientUrl());
model.addAttribute("camps", service.getAllCamps(true)); model.addAttribute("camps", service.getAllCamps(true));
return "/dashboard"; return "/dashboard";
} }

View File

@ -26,6 +26,7 @@ import org.jooq.Record2;
import org.jooq.SelectOrderByStep; import org.jooq.SelectOrderByStep;
import org.jooq.SelectSeekStep1; import org.jooq.SelectSeekStep1;
import org.jooq.SelectSeekStep2; import org.jooq.SelectSeekStep2;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -34,6 +35,8 @@ import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumDocument; import de.jottyfan.camporganizer.db.jooq.enums.EnumDocument;
import de.jottyfan.camporganizer.db.jooq.tables.TProfile; import de.jottyfan.camporganizer.db.jooq.tables.TProfile;
import de.jottyfan.camporganizer.db.jooq.tables.records.VCampRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.VCampRecord;
import de.jottyfan.camporganizer.module.camplist.model.BookingBean;
import de.jottyfan.camporganizer.module.camplist.model.DocumentBean;
/** /**
* *
@ -91,7 +94,7 @@ public class CamplistGateway {
.leftJoin(T_PERSON).on(T_PERSON.FK_PROFILE.eq(T_PROFILE.PK)) .leftJoin(T_PERSON).on(T_PERSON.FK_PROFILE.eq(T_PROFILE.PK))
.leftJoin(REGISTRATOR).on(REGISTRATOR.PK.eq(T_PERSON.FK_REGISTRATOR)) .leftJoin(REGISTRATOR).on(REGISTRATOR.PK.eq(T_PERSON.FK_REGISTRATOR))
.leftJoin(V_CAMP).on(V_CAMP.PK.eq(T_PERSON.FK_CAMP)) .leftJoin(V_CAMP).on(V_CAMP.PK.eq(T_PERSON.FK_CAMP))
.where(T_PROFILE.USERNAME.eq(username)) .where(DSL.trim(T_PROFILE.USERNAME).eq(username == null ? null : username.trim()))
.and(T_PERSON.PK.isNotNull()) .and(T_PERSON.PK.isNotNull())
.orderBy(V_CAMP.ARRIVE.desc(), T_PERSON.CREATED); .orderBy(V_CAMP.ARRIVE.desc(), T_PERSON.CREATED);
// @formatter:on // @formatter:on

View File

@ -13,6 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.db.jooq.tables.records.VCampRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.VCampRecord;
import de.jottyfan.camporganizer.module.camplist.model.BookingBean;
import de.jottyfan.camporganizer.module.dashboard.DashboardRepository; import de.jottyfan.camporganizer.module.dashboard.DashboardRepository;
/** /**

View File

@ -1,57 +1,87 @@
package de.jottyfan.camporganizer.module.camplist; package de.jottyfan.camporganizer.module.camplist;
import javax.servlet.http.HttpServletRequest; import java.security.Principal;
import org.keycloak.KeycloakSecurityContext; 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.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import de.jottyfan.camporganizer.module.registration.KeycloakRepository;
/** /**
* *
* @author jotty * @author jotty
* *
*/ */
public abstract class CommonController { public abstract class CommonController {
private static final Logger LOGGER = LogManager.getLogger();
@Autowired @Autowired
private HttpServletRequest request; private KeycloakRepository keycloak;
/** /**
* try to get current keycloak user * try to get current keycloak user
* *
* @param request the request * @param principal the principal
* @return the preferred username or null * @return the preferred username or null
*/ */
public String getCurrentUser(HttpServletRequest request) { public String getCurrentUser(Principal principal) {
KeycloakSecurityContext ksc = (KeycloakSecurityContext) request return principal == null ? null : principal.getName();
.getAttribute(KeycloakSecurityContext.class.getName());
return ksc == null ? null : ksc.getIdToken().getPreferredUsername();
} }
/** /**
* try to get th currnt keycloak email * try to get th currnt keycloak email
* *
* @param request the request * @param principal the principal
* @return the email or null * @return the email or null
*/ */
public String getCurrentEmail(HttpServletRequest request) { public String getCurrentEmail(Principal principal) {
KeycloakSecurityContext ksc = (KeycloakSecurityContext) request if (principal instanceof OAuth2AuthenticationToken) {
.getAttribute(KeycloakSecurityContext.class.getName()); OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) principal;
return ksc == null ? null : ksc.getIdToken().getEmail(); if (token != null) {
OAuth2User user = token.getPrincipal();
if (user != null) {
return user.getAttribute("email");
}
}
} else {
LOGGER.error("could not find email for {}", principal);
}
return null;
} }
@ModelAttribute("currentUser") @ModelAttribute("currentUser")
public String getCurrentUser() { public String getCurrentUser() {
return getCurrentUser(request); SecurityContext context = SecurityContextHolder.getContext();
if (context != null) {
Authentication authentication = context.getAuthentication();
if (authentication != null) {
DefaultOidcUser dou = (DefaultOidcUser) authentication.getPrincipal();
return dou == null ? null : dou.getName();
}
}
return null;
}
@ModelAttribute("keycloakProfileUrl")
public String getKeycloakProfileUrl() {
return keycloak.getUserClientUrl();
} }
/** /**
* return true if the user has a valid keycloak session token * return true if the user has a valid keycloak session token
* *
* @param request the request * @param principal the principal
* @return true or false * @return true or false
*/ */
public boolean isLoggedIn(HttpServletRequest request) { public boolean isLoggedIn(Principal principal) {
return getCurrentUser(request) != null; return getCurrentUser(principal) != null;
} }
} }

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.camplist; package de.jottyfan.camporganizer.module.camplist.model;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDate; import java.time.LocalDate;

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.camplist; package de.jottyfan.camporganizer.module.camplist.model;
import java.io.Serializable; import java.io.Serializable;

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.camplist; package de.jottyfan.camporganizer.module.camplist.model;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.camplist; package de.jottyfan.camporganizer.module.camplist.model;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.info.BuildProperties; import org.springframework.boot.info.BuildProperties;

View File

@ -3,15 +3,13 @@ package de.jottyfan.camporganizer.module.confirmation.confirmation;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest;
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.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import de.jottyfan.camporganizer.module.camplist.CommonController; import de.jottyfan.camporganizer.module.camplist.CommonController;
import de.jottyfan.camporganizer.module.confirmation.confirmation.impl.CampOverviewBean; import de.jottyfan.camporganizer.module.confirmation.confirmation.model.CampOverviewBean;
/** /**
* *
@ -22,14 +20,11 @@ import de.jottyfan.camporganizer.module.confirmation.confirmation.impl.CampOverv
public class ConfirmationController extends CommonController { public class ConfirmationController extends CommonController {
@Autowired @Autowired
private HttpServletRequest request; private ConfirmationService indexService;
@Autowired
private IConfirmationService indexService;
@GetMapping("/confirmation") @GetMapping("/confirmation")
public String getIndex(Model model) { public String getIndex(Model model) {
List<CampOverviewBean> campoverview = indexService.getCampOverview(request); List<CampOverviewBean> campoverview = indexService.getCampOverview(super.getCurrentUser());
CampOverviewBean campoverviewsummary = new CampOverviewBean(LocalDate.now(), "summary"); CampOverviewBean campoverviewsummary = new CampOverviewBean(LocalDate.now(), "summary");
for (CampOverviewBean bean : campoverview) { for (CampOverviewBean bean : campoverview) {
campoverviewsummary.setApproved(bean.getApproved() + campoverviewsummary.getApproved()); campoverviewsummary.setApproved(bean.getApproved() + campoverviewsummary.getApproved());
@ -38,9 +33,9 @@ public class ConfirmationController extends CommonController {
} }
model.addAttribute("campoverview", campoverview); model.addAttribute("campoverview", campoverview);
model.addAttribute("campoverviewsummary", campoverviewsummary); model.addAttribute("campoverviewsummary", campoverviewsummary);
model.addAttribute("untouched", indexService.getUntouched(request)); model.addAttribute("untouched", indexService.getUntouched(super.getCurrentUser()));
model.addAttribute("approved", indexService.getApproved(request)); model.addAttribute("approved", indexService.getApproved(super.getCurrentUser()));
model.addAttribute("rejected", indexService.getRejected(request)); model.addAttribute("rejected", indexService.getRejected(super.getCurrentUser()));
return "confirmation/confirmation"; return "confirmation/confirmation";
} }
} }

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.confirmation.confirmation.impl; package de.jottyfan.camporganizer.module.confirmation.confirmation;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP; import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPPROFILE; import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPPROFILE;
@ -30,6 +30,8 @@ import org.springframework.transaction.annotation.Transactional;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole; import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumModule; import de.jottyfan.camporganizer.db.jooq.enums.EnumModule;
import de.jottyfan.camporganizer.module.confirmation.confirmation.model.BookingBean;
import de.jottyfan.camporganizer.module.confirmation.confirmation.model.CampOverviewBean;
/** /**
* *

View File

@ -1,15 +1,16 @@
package de.jottyfan.camporganizer.module.confirmation.confirmation.impl; package de.jottyfan.camporganizer.module.confirmation.confirmation;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.jooq.exception.DataAccessException; import org.jooq.exception.DataAccessException;
import org.keycloak.KeycloakSecurityContext; import org.keycloak.KeycloakSecurityContext;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.module.confirmation.confirmation.IConfirmationService; import de.jottyfan.camporganizer.module.confirmation.confirmation.model.BookingBean;
import de.jottyfan.camporganizer.module.confirmation.confirmation.model.CampOverviewBean;
/** /**
* *
@ -17,45 +18,30 @@ import de.jottyfan.camporganizer.module.confirmation.confirmation.IConfirmationS
* *
*/ */
@Service @Service
public class ConfirmationService implements IConfirmationService { public class ConfirmationService {
@Autowired @Autowired
private ConfirmationRepository gateway; private ConfirmationRepository gateway;
@Override public List<CampOverviewBean> getCampOverview(String currentUser) {
public String getCurrentUser(HttpServletRequest request) { return gateway.getCampOverviewBeans(currentUser);
KeycloakSecurityContext ksc = (KeycloakSecurityContext) request
.getAttribute(KeycloakSecurityContext.class.getName());
if (ksc == null) {
throw new DataAccessException("no keycloak user in session");
}
return ksc.getIdToken().getPreferredUsername();
} }
@Override public List<BookingBean> getUntouched(String currentUser) {
public List<CampOverviewBean> getCampOverview(HttpServletRequest request) { return gateway.getUntouched(currentUser);
return gateway.getCampOverviewBeans(getCurrentUser(request));
} }
@Override public List<BookingBean> getApproved(String currentUser) {
public List<BookingBean> getUntouched(HttpServletRequest request) { return gateway.getApproved(currentUser);
return gateway.getUntouched(getCurrentUser(request));
} }
@Override public List<BookingBean> getRejected(String currentUser) {
public List<BookingBean> getApproved(HttpServletRequest request) { return gateway.getRejected(currentUser);
return gateway.getApproved(getCurrentUser(request));
} }
@Override public String search(String needle, String linkURL, String currentUser) {
public List<BookingBean> getRejected(HttpServletRequest request) {
return gateway.getRejected(getCurrentUser(request));
}
@Override
public String search(String needle, String linkURL, HttpServletRequest request) {
StringBuilder buf = new StringBuilder( StringBuilder buf = new StringBuilder(
"<table class=\"table table-striped\"><thead><tr><th>Dabei</th><th>Name</th><th>Freizeit</th><th>Rolle</th></tr><tbody>"); "<table class=\"table table-striped\"><thead><tr><th>Dabei</th><th>Name</th><th>Freizeit</th><th>Rolle</th></tr><tbody>");
for (BookingBean bean : gateway.getSearchResult(needle, getCurrentUser(request))) { for (BookingBean bean : gateway.getSearchResult(needle, currentUser)) {
String acceptHtml = ""; String acceptHtml = "";
if (bean.getAccept() == null) { if (bean.getAccept() == null) {
acceptHtml = "<i class=\"fas fa-question framed framed-orange\"></i>"; acceptHtml = "<i class=\"fas fa-question framed framed-orange\"></i>";

View File

@ -1,65 +0,0 @@
package de.jottyfan.camporganizer.module.confirmation.confirmation;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import de.jottyfan.camporganizer.module.confirmation.confirmation.impl.BookingBean;
import de.jottyfan.camporganizer.module.confirmation.confirmation.impl.CampOverviewBean;
/**
*
* @author jotty
*
*/
public interface IConfirmationService {
/**
* get the user of this session
*
* @param request the request
* @return the username of the current user
*/
public String getCurrentUser(HttpServletRequest request);
/**
* get a list of the camps and its booking status
*
* @param request the request
* @return the camp overview beans
*/
public List<CampOverviewBean> getCampOverview(HttpServletRequest request);
/**
* get a list of bookings that have not yet been worked on
*
* @param request the request
* @return the list of untouched bookings
*/
public List<BookingBean> getUntouched(HttpServletRequest request);
/**
* get a list of approved bookings
*
* @param request the request
* @return the list of approved bookings
*/
public List<BookingBean> getApproved(HttpServletRequest request);
/**
* get a list of rejected bookings
*
* @param request the request
* @return the list of rejected bookings
*/
public List<BookingBean> getRejected(HttpServletRequest request);
/**
* get the result of a search for needle in the database
*
* @param needle the needle; may be a name of anything
* @param linkURL the URL of the link for clicking on the found entity
* @param request the request
* @return the result in html format (for now)
*/
public String search(String needle, String linkURL, HttpServletRequest request);
}

View File

@ -1,31 +1,32 @@
package de.jottyfan.camporganizer.module.confirmation.confirmation; package de.jottyfan.camporganizer.module.confirmation.confirmation;
import javax.servlet.http.HttpServletRequest;
import javax.websocket.server.PathParam;
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.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import de.jottyfan.camporganizer.module.camplist.CommonController;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.websocket.server.PathParam;
/** /**
* *
* @author jotty * @author jotty
* *
*/ */
@Controller @Controller
public class SearchController { public class SearchController extends CommonController {
@Autowired @Autowired
private HttpServletRequest request; private HttpServletRequest request;
@Autowired @Autowired
private IConfirmationService service; private ConfirmationService service;
@GetMapping("/confirmation/search") @GetMapping("/confirmation/search")
@ResponseBody @ResponseBody
public String search(@PathParam(value = "needle") String needle, Model model) { public String search(@PathParam(value = "needle") String needle, Model model) {
return service.search(needle, request.getRequestURI().replace("search", "person"), request); return service.search(needle, request.getRequestURI().replace("search", "person"), super.getCurrentUser());
} }
} }

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.confirmation.confirmation.impl; package de.jottyfan.camporganizer.module.confirmation.confirmation.model;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDate; import java.time.LocalDate;

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.confirmation.confirmation.impl; package de.jottyfan.camporganizer.module.confirmation.confirmation.model;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDate; import java.time.LocalDate;

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.confirmation.confirmation.impl; package de.jottyfan.camporganizer.module.confirmation.confirmation.model;
import java.io.Serializable; import java.io.Serializable;

View File

@ -1,7 +1,5 @@
package de.jottyfan.camporganizer.module.confirmation.person; package de.jottyfan.camporganizer.module.confirmation.person;
import javax.servlet.http.HttpServletRequest;
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.ui.Model;
@ -11,9 +9,9 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import de.jottyfan.camporganizer.module.camplist.CommonController; import de.jottyfan.camporganizer.module.camplist.CommonController;
import de.jottyfan.camporganizer.module.confirmation.confirmation.IConfirmationService; import de.jottyfan.camporganizer.module.confirmation.confirmation.ConfirmationService;
import de.jottyfan.camporganizer.module.confirmation.person.impl.PersonBean; import de.jottyfan.camporganizer.module.confirmation.person.model.PersonBean;
import de.jottyfan.camporganizer.module.confirmation.person.impl.PersonService; import jakarta.servlet.http.HttpServletRequest;
/** /**
* *
@ -23,18 +21,12 @@ import de.jottyfan.camporganizer.module.confirmation.person.impl.PersonService;
@Controller @Controller
public class PersonController extends CommonController { public class PersonController extends CommonController {
@Autowired
private HttpServletRequest request;
@Autowired
private IConfirmationService confirmationService;
@Autowired @Autowired
private PersonService personService; private PersonService personService;
@GetMapping("/confirmation/person/{pk}") @GetMapping("/confirmation/person/{pk}")
public String getIndex(Model model, @PathVariable Integer pk) { public String getIndex(Model model, @PathVariable Integer pk) {
String username = confirmationService.getCurrentUser(request); String username = super.getCurrentUser();
model.addAttribute("currentUser", username); model.addAttribute("currentUser", username);
model.addAttribute("person", personService.getPerson(username, pk)); model.addAttribute("person", personService.getPerson(username, pk));
model.addAttribute("camps", personService.getCamps(username)); model.addAttribute("camps", personService.getCamps(username));
@ -44,7 +36,7 @@ public class PersonController extends CommonController {
@PostMapping("/confirmation/person/update") @PostMapping("/confirmation/person/update")
public String doUpdate(@ModelAttribute PersonBean bean, Model model) { public String doUpdate(@ModelAttribute PersonBean bean, Model model) {
String username = confirmationService.getCurrentUser(request); String username = super.getCurrentUser();
personService.updatePerson(bean, username); personService.updatePerson(bean, username);
return "redirect:/confirmation"; return "redirect:/confirmation";
} }

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.confirmation.person.impl; package de.jottyfan.camporganizer.module.confirmation.person;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP; import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPPROFILE; import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPPROFILE;
@ -36,7 +36,9 @@ 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.TPersonRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TProfileRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TProfileRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TRssRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TRssRecord;
import de.jottyfan.camporganizer.module.camplist.LambdaResultWrapper; import de.jottyfan.camporganizer.module.camplist.model.LambdaResultWrapper;
import de.jottyfan.camporganizer.module.confirmation.person.model.CampBean;
import de.jottyfan.camporganizer.module.confirmation.person.model.PersonBean;
import de.jottyfan.camporganizer.module.mail.MailBean; import de.jottyfan.camporganizer.module.mail.MailBean;
import de.jottyfan.camporganizer.module.mail.MailRepository; import de.jottyfan.camporganizer.module.mail.MailRepository;

View File

@ -1,10 +1,13 @@
package de.jottyfan.camporganizer.module.confirmation.person.impl; package de.jottyfan.camporganizer.module.confirmation.person;
import java.util.List; import java.util.List;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.module.confirmation.person.model.CampBean;
import de.jottyfan.camporganizer.module.confirmation.person.model.PersonBean;
/** /**
* *
* @author jotty * @author jotty

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.confirmation.person.impl; package de.jottyfan.camporganizer.module.confirmation.person.model;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.confirmation.person.impl; package de.jottyfan.camporganizer.module.confirmation.person.model;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDate; import java.time.LocalDate;

View File

@ -21,8 +21,8 @@ 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.TPersonRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersondocumentRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TPersondocumentRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TRssRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TRssRecord;
import de.jottyfan.camporganizer.module.camplist.BookingBean; import de.jottyfan.camporganizer.module.camplist.model.BookingBean;
import de.jottyfan.camporganizer.module.camplist.LambdaResultWrapper; import de.jottyfan.camporganizer.module.camplist.model.LambdaResultWrapper;
/** /**
* *

View File

@ -4,7 +4,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Base64; import java.util.Base64;
import javax.servlet.http.Part; import jakarta.servlet.http.Part;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;

View File

@ -2,7 +2,7 @@ package de.jottyfan.camporganizer.module.document;
import java.util.Base64; import java.util.Base64;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ByteArrayResource;

View File

@ -2,7 +2,7 @@ package de.jottyfan.camporganizer.module.ical;
import java.io.IOException; import java.io.IOException;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -19,7 +19,7 @@ import de.jottyfan.camporganizer.module.camplist.CommonController;
public class ICalController extends CommonController { public class ICalController extends CommonController {
@Autowired @Autowired
private IICalService service; private ICalService service;
/** /**
* generate the ical response stream * generate the ical response stream

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.ical.impl; package de.jottyfan.camporganizer.module.ical;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP; import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_LOCATION; import static de.jottyfan.camporganizer.db.jooq.Tables.T_LOCATION;

View File

@ -1,15 +1,13 @@
package de.jottyfan.camporganizer.module.ical.impl; package de.jottyfan.camporganizer.module.ical;
import java.io.IOException; import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import biweekly.Biweekly; import biweekly.Biweekly;
import biweekly.ICalendar; import biweekly.ICalendar;
import de.jottyfan.camporganizer.module.ical.IICalService; import jakarta.servlet.http.HttpServletResponse;
/** /**
* *
@ -17,12 +15,11 @@ import de.jottyfan.camporganizer.module.ical.IICalService;
* *
*/ */
@Service @Service
public class ICalService implements IICalService { public class ICalService {
@Autowired @Autowired
private ICalRepository gateway; private ICalRepository gateway;
@Override
public Boolean generate(HttpServletResponse response) throws IOException { public Boolean generate(HttpServletResponse response) throws IOException {
ICalendar ical = gateway.getIcal(); ICalendar ical = gateway.getIcal();
String content = Biweekly.write(ical).go(); String content = Biweekly.write(ical).go();
@ -37,5 +34,4 @@ public class ICalService implements IICalService {
return true; return true;
} }
} }

View File

@ -1,23 +0,0 @@
package de.jottyfan.camporganizer.module.ical;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author jotty
*
*/
public interface IICalService {
/**
* generate the ical
*
* @param response the response for the output stream
*
* @return true if successful, false otherwise
* @throws IOException on io errors
*/
public Boolean generate(HttpServletResponse response) throws IOException;
}

View File

@ -5,8 +5,8 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotEmpty;
/** /**
* *

View File

@ -3,8 +3,8 @@ package de.jottyfan.camporganizer.module.mail;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Set; import java.util.Set;
import javax.mail.MessagingException; import jakarta.mail.MessagingException;
import javax.mail.internet.MimeMessage; import jakarta.mail.internet.MimeMessage;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;

View File

@ -2,8 +2,8 @@ package de.jottyfan.camporganizer.module.migration;
import java.io.Serializable; import java.io.Serializable;
import javax.validation.constraints.Email; import jakarta.validation.constraints.Email;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import org.jasypt.exceptions.EncryptionOperationNotPossibleException; import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
import org.jasypt.util.password.StrongPasswordEncryptor; import org.jasypt.util.password.StrongPasswordEncryptor;

View File

@ -1,6 +1,6 @@
package de.jottyfan.camporganizer.module.migration; package de.jottyfan.camporganizer.module.migration;
import javax.validation.Valid; import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;

View File

@ -1,6 +1,6 @@
package de.jottyfan.camporganizer.module.migration; package de.jottyfan.camporganizer.module.migration;
import javax.validation.Valid; import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;

View File

@ -3,8 +3,8 @@ package de.jottyfan.camporganizer.module.registration;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import javax.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.Status;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -27,7 +27,7 @@ import org.springframework.stereotype.Repository;
public class KeycloakRepository { public class KeycloakRepository {
private final static Logger LOGGER = LogManager.getLogger(KeycloakRepository.class); private final static Logger LOGGER = LogManager.getLogger(KeycloakRepository.class);
@Value("${keycloak.resource}") @Value("${keycloak.client-id}")
private String keycloakClientId; private String keycloakClientId;
@Value("${keycloak.auth-server-url}") @Value("${keycloak.auth-server-url}")

View File

@ -1,6 +1,6 @@
package de.jottyfan.camporganizer.module.registration; package de.jottyfan.camporganizer.module.registration;
import javax.validation.Valid; import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -13,6 +13,8 @@ import org.springframework.web.bind.annotation.PostMapping;
import de.jottyfan.camporganizer.db.EnumConverter; import de.jottyfan.camporganizer.db.EnumConverter;
import de.jottyfan.camporganizer.module.camplist.CommonController; import de.jottyfan.camporganizer.module.camplist.CommonController;
import de.jottyfan.camporganizer.module.registration.model.CampBean;
import de.jottyfan.camporganizer.module.registration.model.RegistrationBean;
/** /**
* *
@ -42,6 +44,18 @@ public class RegistrationController extends CommonController {
} }
} }
/**
* userlogin is protected and leads the request to the keycloak login mask if not yet logged in
*
* @param fkCamp the ID of the camp
* @param model the model
* @return hen registration page
*/
@GetMapping("/userlogin/registration/{fkCamp}")
public String loginIndex(@PathVariable(name = "fkCamp", required = true) Integer fkCamp, Model model) {
return index(fkCamp, model);
}
@PostMapping("/registration/register") @PostMapping("/registration/register")
public String register(@Valid @ModelAttribute("bean") RegistrationBean bean, final BindingResult bindingResult, public String register(@Valid @ModelAttribute("bean") RegistrationBean bean, final BindingResult bindingResult,
Model model) { Model model) {

View File

@ -43,8 +43,11 @@ 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.TPersondocumentRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TProfileRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TProfileRecord;
import de.jottyfan.camporganizer.db.jooq.tables.records.TRssRecord; import de.jottyfan.camporganizer.db.jooq.tables.records.TRssRecord;
import de.jottyfan.camporganizer.module.camplist.BookingBean; import de.jottyfan.camporganizer.module.camplist.model.BookingBean;
import de.jottyfan.camporganizer.module.camplist.LambdaResultWrapper; import de.jottyfan.camporganizer.module.camplist.model.LambdaResultWrapper;
import de.jottyfan.camporganizer.module.registration.model.CampBean;
import de.jottyfan.camporganizer.module.registration.model.ProfileBean;
import de.jottyfan.camporganizer.module.registration.model.RegistrationBean;
/** /**
* *
@ -91,7 +94,7 @@ public class RegistrationRepository {
SelectConditionStep<TProfileRecord> sql = jooq SelectConditionStep<TProfileRecord> sql = jooq
// @formatter:off // @formatter:off
.selectFrom(T_PROFILE) .selectFrom(T_PROFILE)
.where(T_PROFILE.USERNAME.eq(login)); .where(DSL.lower(T_PROFILE.USERNAME).eq(login));
// @formatter:on // @formatter:on
LOGGER.debug(sql); LOGGER.debug(sql);
return sql.fetch().size() < 1; return sql.fetch().size() < 1;
@ -108,9 +111,9 @@ public class RegistrationRepository {
LambdaResultWrapper lrw = new LambdaResultWrapper(); LambdaResultWrapper lrw = new LambdaResultWrapper();
jooq.transaction(t -> { jooq.transaction(t -> {
if (bean.getLogin() != null && !bean.getLogin().isEmpty()) { if (bean.getLogin() != null && !bean.getLogin().isEmpty()) {
Boolean loginNotYetInUse = isLoginNotYetInUse(bean.getLogin()); Boolean loginNotYetInUse = isLoginNotYetInUse(bean.getLogin().toLowerCase());
if (bean.getRegisterInKeycloak() && !loginNotYetInUse) { if (bean.getRegisterInKeycloak() && !loginNotYetInUse) {
throw new DataAccessException("login already in use: " + bean.getLogin()); throw new DataAccessException("login already in use: " + bean.getLogin().toLowerCase());
} }
Integer fkProfile = null; Integer fkProfile = null;
if (loginNotYetInUse) { if (loginNotYetInUse) {
@ -124,7 +127,7 @@ public class RegistrationRepository {
T_PROFILE.PASSWORD, T_PROFILE.PASSWORD,
T_PROFILE.DUEDATE, T_PROFILE.DUEDATE,
T_PROFILE.UUID) T_PROFILE.UUID)
.values(bean.getForename(), bean.getSurname(), bean.getLogin(), oldPassword, LocalDateTime.now().plus(356, ChronoUnit.DAYS), UUID.nameUUIDFromBytes(bean.getLogin().getBytes()).toString()) .values(bean.getForename(), bean.getSurname(), bean.getLogin().toLowerCase(), oldPassword, LocalDateTime.now().plus(356, ChronoUnit.DAYS), UUID.nameUUIDFromBytes(bean.getLogin().getBytes()).toString())
.returning(T_PROFILE.PK); .returning(T_PROFILE.PK);
// @formatter:on // @formatter:on
LOGGER.debug(sql1.toString()); LOGGER.debug(sql1.toString());
@ -144,7 +147,7 @@ public class RegistrationRepository {
// @formatter:off // @formatter:off
.select(T_PROFILE.PK) .select(T_PROFILE.PK)
.from(T_PROFILE) .from(T_PROFILE)
.where(T_PROFILE.USERNAME.eq(bean.getLogin())); .where(DSL.lower(T_PROFILE.USERNAME).eq(bean.getLogin().toLowerCase()));
// @formatter:on // @formatter:on
LOGGER.debug(sql1.toString()); LOGGER.debug(sql1.toString());
fkProfile = sql1.fetchOne().get(T_PROFILE.PK); fkProfile = sql1.fetchOne().get(T_PROFILE.PK);

View File

@ -3,7 +3,9 @@ package de.jottyfan.camporganizer.module.registration;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import de.jottyfan.camporganizer.module.camplist.BookingBean; import de.jottyfan.camporganizer.module.camplist.model.BookingBean;
import de.jottyfan.camporganizer.module.registration.model.CampBean;
import de.jottyfan.camporganizer.module.registration.model.RegistrationBean;
/** /**
* *
@ -52,7 +54,7 @@ public class RegistrationService {
} }
Boolean result = gateway.register(bean); Boolean result = gateway.register(bean);
if (result && bean.getRegisterInKeycloak()) { if (result && bean.getRegisterInKeycloak()) {
keycloak.register(bean.getForename(), bean.getSurname(), bean.getLogin(), bean.getPassword(), bean.getEmail()); keycloak.register(bean.getKcForename(), bean.getKcSurname(), bean.getLogin(), bean.getPassword(), bean.getKcEmail());
} }
return result; return result;
} }

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.registration; package de.jottyfan.camporganizer.module.registration.model;
import java.io.Serializable; import java.io.Serializable;

View File

@ -1,4 +1,4 @@
package de.jottyfan.camporganizer.module.registration; package de.jottyfan.camporganizer.module.registration.model;
import java.io.Serializable; import java.io.Serializable;

View File

@ -1,11 +1,11 @@
package de.jottyfan.camporganizer.module.registration; package de.jottyfan.camporganizer.module.registration.model;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDate; import java.time.LocalDate;
import javax.validation.constraints.Email; import jakarta.validation.constraints.Email;
import javax.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
@ -21,6 +21,7 @@ import de.jottyfan.camporganizer.module.registration.validate.UnusedUsername;
*/ */
@UnusedUsername(field = "login", message = "Dieses Login ist leider bereits vergeben. Bitte wähle ein anderes.") @UnusedUsername(field = "login", message = "Dieses Login ist leider bereits vergeben. Bitte wähle ein anderes.")
@TeacherAgeCheck(field = "birthDate", fkCamp = "fkCamp", campRole = "campRole", message = "Als Mitarbeiter bist Du leider zu jung für diese Freizeit.") @TeacherAgeCheck(field = "birthDate", fkCamp = "fkCamp", campRole = "campRole", message = "Als Mitarbeiter bist Du leider zu jung für diese Freizeit.")
// TODO: registration completeness annotation; in case of registerInKeycloak == true, force login, password, kcForename, kcSurname and kcEmail not to be blank
public class RegistrationBean implements Serializable { public class RegistrationBean implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -50,6 +51,9 @@ public class RegistrationBean implements Serializable {
private Boolean registerInKeycloak; private Boolean registerInKeycloak;
private String login; private String login;
private String password; private String password;
private String kcForename;
private String kcSurname;
private String kcEmail;
/** /**
* @return forename + surname, separated by a space * @return forename + surname, separated by a space
@ -267,4 +271,46 @@ public class RegistrationBean implements Serializable {
public void setCampRole(EnumCamprole campRole) { public void setCampRole(EnumCamprole campRole) {
this.campRole = campRole; this.campRole = campRole;
} }
/**
* @return the kcForename
*/
public String getKcForename() {
return kcForename;
}
/**
* @param kcForename the kcForename to set
*/
public void setKcForename(String kcForename) {
this.kcForename = kcForename;
}
/**
* @return the kcSurname
*/
public String getKcSurname() {
return kcSurname;
}
/**
* @param kcSurname the kcSurname to set
*/
public void setKcSurname(String kcSurname) {
this.kcSurname = kcSurname;
}
/**
* @return the kcEmail
*/
public String getKcEmail() {
return kcEmail;
}
/**
* @param kcEmail the kcEmail to set
*/
public void setKcEmail(String kcEmail) {
this.kcEmail = kcEmail;
}
} }

View File

@ -6,8 +6,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import javax.validation.Constraint; import jakarta.validation.Constraint;
import javax.validation.Payload; import jakarta.validation.Payload;
/** /**
* *

View File

@ -2,8 +2,8 @@ package de.jottyfan.camporganizer.module.registration.validate;
import java.time.LocalDate; import java.time.LocalDate;
import javax.validation.ConstraintValidator; import jakarta.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext; import jakarta.validation.ConstraintValidatorContext;
import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@ -6,8 +6,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import javax.validation.Constraint; import jakarta.validation.Constraint;
import javax.validation.Payload; import jakarta.validation.Payload;
/** /**
* *

View File

@ -1,7 +1,7 @@
package de.jottyfan.camporganizer.module.registration.validate; package de.jottyfan.camporganizer.module.registration.validate;
import javax.validation.ConstraintValidator; import jakarta.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext; import jakarta.validation.ConstraintValidatorContext;
import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;

View File

@ -1,6 +1,6 @@
package de.jottyfan.camporganizer.module.rss; package de.jottyfan.camporganizer.module.rss;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;

View File

@ -5,7 +5,7 @@ import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;

View File

@ -9,11 +9,19 @@ spring.datasource.password = ${spring.datasource.password}
server.servlet.context-path = ${server.servlet.context-path:/CampOrganizer2} server.servlet.context-path = ${server.servlet.context-path:/CampOrganizer2}
# security
keycloak.auth-server-url = ${keycloak.auth-server-url} keycloak.auth-server-url = ${keycloak.auth-server-url}
keycloak.realm = ${keycloak.realm:ow} keycloak.realm = ${keycloak.realm}
keycloak.resource = ${keycloak.resource:biblecamp} spring.security.oauth2.client.registration.keycloak.client-id = ${keycloak.client-id}
keycloak.public-client = ${keycloak.public-client} spring.security.oauth2.client.registration.keycloak.scope = openid
keycloak.use-resource-role-mappings = ${keycloak.use-resource-role-mappings} spring.security.oauth2.client.registration.keycloak.authorization-grant-type = authorization_code
spring.security.oauth2.client.registration.keycloak.redirect-uri = ${keycloak.redirect-uri}
spring.security.oauth2.client.provider.keycloak.issuer-uri = ${keycloak.issuer-uri}
spring.security.oauth2.client.provider.keycloak.authorization-uri = ${keycloak.openid-url}/auth
spring.security.oauth2.client.provider.keycloak.token-uri = ${keycloak.openid-url}/token
spring.security.oauth2.client.provider.keycloak.user-info-uri = ${keycloak.openid-url}/userinfo
spring.security.oauth2.client.provider.keycloak.jwk-set-uri = ${keycloak.openid-url}/certs
spring.security.oauth2.client.provider.keycloak.user-name-attribute = preferred_username
ow.keycloak.admin.name = ${ow.keycloak.admin.name} ow.keycloak.admin.name = ${ow.keycloak.admin.name}
ow.keycloak.admin.password = ${ow.keycloak.admin.password} ow.keycloak.admin.password = ${ow.keycloak.admin.password}

View File

@ -433,3 +433,12 @@ div {
box-shadow: 0px 0px 7px 4px #ddd; box-shadow: 0px 0px 7px 4px #ddd;
border-radius: 40px; border-radius: 40px;
} }
.beforetext {
margin-right: 8px;
}
.visibledropdown {
max-height: 80vh;
overflow: auto;
}

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Ebene_2" data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 236.23 143.81">
<defs>
<style>
.cls-1 {
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 2px;
}
.cls-1, .cls-2 {
fill: none;
stroke: #313122;
}
.cls-2 {
stroke-miterlimit: 10;
stroke-width: 5px;
}
</style>
</defs>
<g id="Ebene_1-2" data-name="Ebene 1">
<g>
<path class="cls-2" d="m30.26,60.8c0-11.68,9.59-19.43,21.41-19.43h7.14c11.83,0,21.41,7.75,21.41,19.43v2.78"/>
<path class="cls-2" d="m91.34,46.92c-8.79,9.48-22.21,33.31-22.21,33.31l44.42,44.42h11.1l61.07-44.42h48v-16.66c0-9.2-7.46-16.66-16.66-16.66,0,0-54.25-11.1-81.32-11.1s-35.92,1.94-44.42,11.1Z"/>
<polyline class="cls-2" points="233.73 80.23 233.73 141.31 217.07 141.31 205.96 113.55 141.31 113.55"/>
<polyline class="cls-2" points="102.44 113.55 30.26 113.55 19.16 141.31 2.5 141.31 2.5 2.5 30.26 2.5 30.26 80.23 69.13 80.23"/>
</g>
<g>
<path class="cls-1" d="m182.39,82.77c.11.2,4.21-2.74,4.34-2.51,1.03,1.79-19.11,31.45-18.07,33.25,1.64,2.84,55.85-36.31,57.61-33.26,1.14,1.97-25.49,31.64-24.56,33.26.88,1.52,31.09-20.02,31.99-18.47.97,1.69-22.64,31.89-21.72,33.49.57.99,21.05-13.73,21.73-12.55.62,1.07-5.44,24.08-4.74,25.29.13.22,4.62-2.98,4.76-2.75"/>
<g>
<path class="cls-1" d="m31.23,113.53c1.4,2.42,43.71-28.47,45.11-26.04.72,1.24-11.09,24.92-10.44,26.03s22.5-14.43,23.13-13.35c.36.62,3.12,12.7,3.49,13.35.19.32,6.09-3.89,6.25-3.61"/>
<path class="cls-1" d="m2.53,3.11s.98-.62,1-.58c.51.88-1.44,17.86-1,18.62.79,1.37,26.89-17.41,27.7-15.99C31.46,7.28,1.41,43.16,2.53,45.1c.85,1.47,26.87-17.45,27.71-16,1.15,1.99-28.78,36.07-27.71,37.92.72,1.25,26.93-17.37,27.72-16,1.26,2.19-28.81,35.35-27.72,37.24.82,1.42,26.87-17.49,27.73-16.01,1.32,2.29-28.82,37.88-27.73,39.77,1.56,2.7,53.39-34.59,55.02-31.77,1.78,3.09-56.85,47.45-55.02,50.61.71,1.23,26.28-16.85,27.01-15.59s-12.02,24.9-11.38,26c.04.07,1.26-.82,1.3-.75"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -5,4 +5,18 @@ class MyToggle {
toggle(divid) { toggle(divid) {
$("[id='" + divid + "']").toggle(); $("[id='" + divid + "']").toggle();
} }
} }
filterAllOfClass = function(filterselector, haystackselector) {
$(haystackselector).each(function() {
var t = $($(this).children()[0]).text();
var filter = $(filterselector).val();
if (filter == '' || filter == '*') {
$(this).show();
} else if (t.toUpperCase().indexOf(filter.toUpperCase()) > -1) {
$(this).show();
} else {
$(this).hide();
}
});
}

View File

@ -14,17 +14,17 @@
</thead> </thead>
<tbody> <tbody>
<tr th:each="c : ${camps}"> <tr th:each="c : ${camps}">
<td><a th:href="@{/admin/camp/edit/{id}(id=${c.pk})}"><span th:text="${c.name}"></span></a></td> <td><a th:href="@{/admin/camp/edit/{id}(id=${c.pk})}"><i class="fas fa-pen beforetext"></i><span th:text="${c.name}"></span></a></td>
<td><th:block th:each="l : ${locations}"> <td><th:block th:each="l : ${locations}">
<span th:if="${l.pk == c.fkLocation}" th:text="${l.name}"></span> <span th:if="${l.pk == c.fkLocation}" th:text="${l.name}"></span>
</th:block></td> </th:block></td>
<td><span th:text="${#temporals.format(c.arrive, 'dd.MM.')}"></span>&nbsp;-&nbsp;<span th:text="${#temporals.format(c.depart, 'dd.MM.yyyy')}"></span></td> <td><span th:text="${#temporals.format(c.arrive, 'dd.MM.')}"></span>&nbsp;-&nbsp;<span th:text="${#temporals.format(c.depart, 'dd.MM.yyyy')}"></span></td>
<td><a th:href="@{/document/{id}(id=${c.fkDocument})}"><i class="fas fa-download"></i></a></td> <td><a th:href="@{/document/{id}(id=${c.fkDocument})}"><i class="fas fa-download beforetext"></i>anzeigen</a></td>
</tr> </tr>
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="6" style="text-align: center"><a th:href="@{/admin/camp/add}" class="btn btn-outline-primary">neue Freizeit anlegen</a></td> <td colspan="4" style="text-align: center"><a th:href="@{/admin/camp/add}" class="btn btn-outline-primary">neue Freizeit anlegen</a></td>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>

View File

@ -10,12 +10,11 @@
<td>Dokumententyp</td> <td>Dokumententyp</td>
<td>Zielgruppe</td> <td>Zielgruppe</td>
<th>Inhalt</th> <th>Inhalt</th>
<th>Dateityp</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr th:each="d : ${documents}"> <tr th:each="d : ${documents}">
<td><a th:href="@{/admin/document/edit/{id}(id=${d.pk})}"><span th:text="${d.name}"></span></a></td> <td><a th:href="@{/admin/document/edit/{id}(id=${d.pk})}"><i class="fas fa-pen beforetext"></i><span th:text="${d.name}"></span></a></td>
<td><span th:if="${d.doctype.literal == 'location'}">Wegbeschreibung</span> <span th:if="${d.doctype.literal == 'camp'}">Bestätigung</span><span <td><span th:if="${d.doctype.literal == 'location'}">Wegbeschreibung</span> <span th:if="${d.doctype.literal == 'camp'}">Bestätigung</span><span
th:if="${d.doctype.literal == 'camppass'}">Freizeitpass</span></td> th:if="${d.doctype.literal == 'camppass'}">Freizeitpass</span></td>
<td><th:block th:each="r : ${d.roles}"> <td><th:block th:each="r : ${d.roles}">
@ -25,13 +24,12 @@
<span th:if="${r.literal == 'feeder'}" class="roleflag">Küche</span> <span th:if="${r.literal == 'feeder'}" class="roleflag">Küche</span>
<span th:if="${r.literal == 'observer'}" class="roleflag">Mitarbeiterkind</span> <span th:if="${r.literal == 'observer'}" class="roleflag">Mitarbeiterkind</span>
</th:block></td> </th:block></td>
<td><a th:href="@{/document/{id}(id=${d.pk})}"><i class="fas fa-download"></i></a></td> <td><a th:href="@{/document/{id}(id=${d.pk})}"><i class="fas fa-download beforetext"></i><span th:text="${d.filetype.literal}" th:if="${d.filetype}"></span>&nbsp;anzeigen</a></td>
<td><span th:text="${d.filetype.literal}" th:if="${d.filetype}"></span></td>
</tr> </tr>
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="6" style="text-align: center"><a th:href="@{/admin/document/add}" class="btn btn-outline-primary">neues Dokument anlegen</a></td> <td colspan="4" style="text-align: center"><a th:href="@{/admin/document/add}" class="btn btn-outline-primary">neues Dokument anlegen</a></td>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>

View File

@ -13,14 +13,14 @@
</thead> </thead>
<tbody> <tbody>
<tr th:each="l : ${locations}"> <tr th:each="l : ${locations}">
<td><a th:href="@{/admin/location/edit/{id}(id=${l.pk})}"><span th:text="${l.name}"></span></a></td> <td><a th:href="@{/admin/location/edit/{id}(id=${l.pk})}"><i class="fas fa-pen beforetext"></i><span th:text="${l.name}"></span></a></td>
<td th:text="${l.url}"></td> <td><a th:href="${l.url}" target="_blank"><i class="fas fa-external-link-alt beforetext"></i><span th:text="${l.url}"></span></a></td>
<td><a th:href="@{/document/{id}(id=${l.fkDocument})}"><i class="fas fa-download"></i></a></td> <td><a th:href="@{/document/{id}(id=${l.fkDocument})}"><i class="fas fa-download beforetext"></i>anzeigen</a></td>
</tr> </tr>
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="6" style="text-align: center"><a th:href="@{/admin/location/add}" class="btn btn-outline-primary">neues Freizeitheim anlegen</a></td> <td colspan="3" style="text-align: center"><a th:href="@{/admin/location/add}" class="btn btn-outline-primary">neues Freizeitheim anlegen</a></td>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>

View File

@ -0,0 +1,66 @@
<!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">
<body>
<th:block layout:fragment="content">
<div sec:authorize="hasRole('admin')">
<form th:action="@{/admin/privileges/insert/{d}(d=${pagedest})}" th:object="${bean}" method="post" enctype="multipart/form-data">
<div class="tablebox">
<div class="container">
<div class="row mb-2">
<div class="col-sm-12">
<div class="alert alter-danger" th:if="${error}" th:text="${error}"></div>
</div>
</div>
<div class="row mb-2">
<label for="inputPerson" class="col-sm-2 col-form-label">Person</label>
<div class="col-sm-10">
<span class="error" th:each="error : ${#fields.errors('fkProfile')}">[[${error}]]<br /></span> <select id="inputPerson" th:field="*{fkProfile}"
th:class="${'form-select ' + (#fields.hasErrors('fkProfile') ? 'inputerror' : '')}">
<option value="">--- bitte wählen ---</option>
<option th:each="l : ${profiles}" th:value="${l.pk}" th:text="${l.fullname}"></option>
</select>
</div>
<script type="text/javascript">
$(document).ready(function() {
$("#inputPerson").select2();
});
</script>
</div>
<div class="row mb-2">
<label for="inputCamp" class="col-sm-2 col-form-label">Freizeit</label>
<div class="col-sm-10">
<span class="error" th:each="error : ${#fields.errors('fkCamp')}">[[${error}]]<br /></span> <select id="inputCamp" th:field="*{fkCamp}"
th:class="${'form-select ' + (#fields.hasErrors('fkCamp') ? 'inputerror' : '')}">
<option value="">--- bitte wählen ---</option>
<option th:each="l : ${camps}" th:value="${l.pk}" th:text="${l.name}"></option>
</select>
</div>
<script type="text/javascript">
$(document).ready(function() {
$("#inputCamp").select2();
});
</script>
</div>
<div class="row mb-2">
<label for="inputModule" class="col-sm-2 col-form-label">Modul</label>
<div class="col-sm-10">
<span class="error" th:each="error : ${#fields.errors('module')}">[[${error}]]<br /></span> <select id="inputModule" th:field="*{module}"
th:class="${'form-select ' + (#fields.hasErrors('module') ? 'inputerror' : '')}">
<option value="">--- bitte wählen ---</option>
<option th:each="l : ${modules}" th:value="${l}" th:text="${l}"></option>
</select>
</div>
</div>
<div class="row mb-2">
<div class="col-sm-2"></div>
<div class="col-sm-10">
<input type="submit" class="btn btn-success" value="Ok" /> <a th:href="@{/admin/privileges/abortinsert/{d}(d=${pagedest})}" class="btn btn-outline-secondary">Abbrechen</a>
</div>
</div>
</div>
</div>
</form>
</div>
</th:block>
</body>
</html>

View File

@ -0,0 +1,71 @@
<!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 Privileges</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<th:block layout:fragment="content">
<div sec:authorize="hasRole('admin')">
<div class="tablebox">
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<span th:text="${selected}" th:if="${selected}"></span> <span th:unless="${selected}">-- bitte wählen --</span>
</button>
<ul class="dropdown-menu">
<li style="padding: 8px"><input type="text" id="needle" onkeyup="filterAllOfClass('#needle', '.haystack')" placeholder="Filter" class="form-control" /></li>
<li th:each="e : ${list}" class="haystack"><a class="dropdown-item" th:href="@{/admin/privileges/campbased/{r}(r=${e.pk})}" th:text="${e.fullname}"></a></li>
</ul>
</div>
<div class="card" th:if="${selected}">
<div class="card-header">
<h1 th:text="${selected}"></h1>
</div>
<div class="card-body">
<table id="table" class="table table-striped">
<thead>
<tr>
<th>Person</th>
<th>Modul</th>
</tr>
</thead>
<tbody>
<tr th:each="m : ${container.map}">
<td th:text="${m.key}"></td>
<td>
<div class="btn-toolbar" role="toolbar">
<div class="btn-group beforetext" role="group" th:each="v : ${m.value}" th:if="${m.value}">
<div class="dropdown">
<button class="btn btn-danger dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fas fa-trash-alt"></i>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" th:href="@{/admin/privileges/delete/{id}/{d}(id=${v.key}, d=${pagedest})}">endgültig löschen</a></li>
</ul>
</div>
<button type="button" class="btn btn-outline-secondary" th:text="${v.value}" disabled></button>
</div>
</div>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2" style="text-align: center"><a th:href="@{/admin/privileges/add/{d}(d=${pagedest})}" class="btn btn-outline-primary">neue Berechtigung vergeben</a></td>
</tr>
</tfoot>
</table>
<script>
$(document).ready(function() {
$("#table").DataTable({
language : locale_de
});
});
</script>
</div>
</div>
</div>
</div>
</th:block>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More