basic contact overview
This commit is contained in:
15
.classpath
15
.classpath
@ -14,23 +14,16 @@
|
|||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="src" output="bin/test" path="src/test/java">
|
<classpathentry kind="src" output="bin/test" path="src/test/java">
|
||||||
<attributes>
|
<attributes>
|
||||||
|
<attribute name="test" value="true"/>
|
||||||
<attribute name="gradle_scope" value="test"/>
|
<attribute name="gradle_scope" value="test"/>
|
||||||
<attribute name="gradle_used_by_scope" value="test"/>
|
<attribute name="gradle_used_by_scope" value="test"/>
|
||||||
<attribute name="test" value="true"/>
|
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="src" output="bin/main" path="src/main/webapp">
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/">
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="gradle_scope" value="main"/>
|
<attribute name="module" value="true"/>
|
||||||
<attribute name="gradle_used_by_scope" value="main,test"/>
|
|
||||||
</attributes>
|
|
||||||
</classpathentry>
|
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11/"/>
|
|
||||||
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>
|
|
||||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer">
|
|
||||||
<attributes>
|
|
||||||
<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
|
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||||
<classpathentry kind="output" path="bin/default"/>
|
<classpathentry kind="output" path="bin/default"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
10
.project
10
.project
@ -10,6 +10,11 @@
|
|||||||
<arguments>
|
<arguments>
|
||||||
</arguments>
|
</arguments>
|
||||||
</buildCommand>
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
<buildCommand>
|
<buildCommand>
|
||||||
<name>org.eclipse.wst.common.project.facet.core.builder</name>
|
<name>org.eclipse.wst.common.project.facet.core.builder</name>
|
||||||
<arguments>
|
<arguments>
|
||||||
@ -20,11 +25,6 @@
|
|||||||
<arguments>
|
<arguments>
|
||||||
</arguments>
|
</arguments>
|
||||||
</buildCommand>
|
</buildCommand>
|
||||||
<buildCommand>
|
|
||||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
|
||||||
<arguments>
|
|
||||||
</arguments>
|
|
||||||
</buildCommand>
|
|
||||||
</buildSpec>
|
</buildSpec>
|
||||||
<natures>
|
<natures>
|
||||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
|
||||||
<project-modules id="moduleCoreId" project-version="1.5.0">
|
|
||||||
<wb-module deploy-name="timetrack">
|
<wb-module deploy-name="timetrack">
|
||||||
<property name="context-root" value="timetrack"/>
|
|
||||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/resources"/>
|
<property name="context-root" value="timetrack"/>
|
||||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/webapp"/>
|
|
||||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/java"/>
|
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/resources"/>
|
||||||
<wb-resource deploy-path="/" source-path="src/main/webapp"/>
|
|
||||||
</wb-module>
|
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/webapp"/>
|
||||||
|
|
||||||
|
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/java"/>
|
||||||
|
|
||||||
|
<wb-resource deploy-path="/" source-path="src/main/webapp"/>
|
||||||
|
|
||||||
|
</wb-module>
|
||||||
|
|
||||||
</project-modules>
|
</project-modules>
|
||||||
|
97
build.gradle
97
build.gradle
@ -1,77 +1,70 @@
|
|||||||
buildscript {
|
plugins {
|
||||||
repositories {
|
id 'org.springframework.boot' version '2.6.5'
|
||||||
mavenLocal()
|
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
|
||||||
mavenCentral()
|
id 'java'
|
||||||
maven { url "https://repo.maven.apache.org/maven2" }
|
|
||||||
maven { url "https://plugins.gradle.org/m2/" }
|
|
||||||
maven {
|
|
||||||
url "https://gitlab.com/jottyfan/libs/-/raw/main"
|
|
||||||
}
|
|
||||||
jcenter()
|
|
||||||
}
|
|
||||||
dependencies {
|
|
||||||
classpath 'com.google.code.gson:gson:2.8.6'
|
|
||||||
classpath 'org.postgresql:postgresql:42.2.19'
|
|
||||||
classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:latest.release'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'java'
|
group = 'de.jottyfan'
|
||||||
apply plugin: 'maven-publish'
|
version = '1.2.0'
|
||||||
apply plugin: 'war'
|
sourceCompatibility = '11'
|
||||||
apply plugin: 'eclipse'
|
|
||||||
|
|
||||||
group = 'jottyfan'
|
ext['spring-framework.version'] = '5.3.18'
|
||||||
version = '1.1.9.1'
|
|
||||||
|
|
||||||
description = """timetrack"""
|
description = """timetrack"""
|
||||||
|
|
||||||
sourceCompatibility = 11
|
sourceCompatibility = 11
|
||||||
targetCompatibility = 11
|
targetCompatibility = 11
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
main.java.srcDirs = ['src/main/java']
|
|
||||||
main.resources.srcDirs = ['src/main/resources', 'src/main/webapp']
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType(JavaCompile) {
|
|
||||||
options.encoding = 'UTF-8'
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url "https://www.jottyfan.de/mvnrepo" }
|
maven { url "https://www.jottyfan.de/mvnrepo" }
|
||||||
maven { url "https://repo.maven.apache.org/maven2" }
|
maven { url "https://repo.maven.apache.org/maven2" }
|
||||||
|
// maven { url "https://gitlab.com/jottyfan/libs/-/raw/main" }
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencyManagement {
|
||||||
|
imports {
|
||||||
|
mavenBom 'org.keycloak.bom:keycloak-adapter-bom:17.0.1'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.jboss.weld.servlet:weld-servlet:2.4.8.Final'
|
implementation 'org.apache.logging.log4j:log4j-api:2.17.1'
|
||||||
|
implementation 'org.apache.logging.log4j:log4j-core:2.17.1'
|
||||||
|
implementation 'org.apache.logging.log4j:log4j-to-slf4j:2.17.1'
|
||||||
|
|
||||||
implementation 'de.jottyfan:timetrackjooq:0.1.0'
|
implementation 'org.webjars:bootstrap:5.1.3'
|
||||||
|
implementation 'org.webjars:font-awesome:5.15.4'
|
||||||
|
implementation 'org.webjars:jquery:3.6.0'
|
||||||
|
implementation 'org.webjars:popper.js:2.9.3'
|
||||||
|
implementation 'org.webjars:datatables:1.11.3'
|
||||||
|
implementation 'org.webjars:jquery-ui:1.13.0'
|
||||||
|
|
||||||
|
implementation 'org.webjars.bowergithub.datatables:datatables:1.10.21'
|
||||||
|
|
||||||
|
implementation 'org.keycloak:keycloak-spring-boot-starter:17.0.1'
|
||||||
|
|
||||||
implementation 'org.apache.myfaces.core:myfaces-api:2.3.8'
|
implementation 'org.springframework.boot:spring-boot-starter-jooq'
|
||||||
implementation 'org.apache.myfaces.core:myfaces-impl:2.3.8'
|
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||||
implementation 'net.bootsfaces:bootsfaces:1.5.0'
|
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||||
implementation 'org.postgresql:postgresql:42.2.19'
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
implementation 'org.jooq:jooq:3.14.4'
|
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
|
||||||
implementation 'com.google.code.gson:gson:2.8.6'
|
implementation 'de.jottyfan:timetrackjooq:0.1.0'
|
||||||
implementation 'org.jasypt:jasypt:1.9.3'
|
|
||||||
implementation 'javax.servlet:javax.servlet-api:4.0.1'
|
|
||||||
implementation 'org.apache.logging.log4j:log4j-core:2.16.0'
|
|
||||||
implementation 'org.apache.logging.log4j:log4j-api:2.16.0'
|
|
||||||
|
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.0-M1'
|
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.0.0'
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.0-M1'
|
|
||||||
|
|
||||||
implementation 'org.postgresql:postgresql:42.2.19'
|
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||||
|
runtimeOnly 'org.postgresql:postgresql'
|
||||||
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
|
testImplementation 'org.springframework.security:spring-security-test'
|
||||||
}
|
}
|
||||||
|
|
||||||
war {
|
test {
|
||||||
archiveName 'timetrack.war'
|
useJUnitPlatform()
|
||||||
manifest {
|
|
||||||
attributes('Implementation-Version': project.version)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add version to manifest
|
||||||
|
springBoot {
|
||||||
|
buildInfo()
|
||||||
|
}
|
||||||
|
20
debian/build.sh
vendored
Executable file
20
debian/build.sh
vendored
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
mkdir -p timetrack/var/lib
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
./gradlew clean build
|
||||||
|
|
||||||
|
G=$(grep "^version =" build.gradle | sed -e "s/version = //g" | sed -e "s/'//g")
|
||||||
|
|
||||||
|
echo "found version $G"
|
||||||
|
|
||||||
|
cp -v build/libs/timetrack-$G.jar debian/timetrack/var/lib/timetrack.jar
|
||||||
|
|
||||||
|
cd debian
|
||||||
|
|
||||||
|
sed -i timetrack/DEBIAN/control -e "s/Version: 0.0.0/Version: $G/"
|
||||||
|
|
||||||
|
V=$(grep "Version" timetrack/DEBIAN/control | sed -e "s/Version: //g")
|
||||||
|
|
||||||
|
fakeroot dpkg -b timetrack timetrack_$V.deb
|
1
debian/timetrack/DEBIAN/conffiles
vendored
Normal file
1
debian/timetrack/DEBIAN/conffiles
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/etc/timetrack.properties
|
9
debian/timetrack/DEBIAN/control
vendored
Normal file
9
debian/timetrack/DEBIAN/control
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Package: timetrack
|
||||||
|
Version: 0.0.0
|
||||||
|
Architecture: amd64
|
||||||
|
Maintainer: Jörg Henke <jottyfan@gmx.de>
|
||||||
|
Depends: default-jdk
|
||||||
|
Section: ship
|
||||||
|
Priority: optional
|
||||||
|
Description: timetrack application to track work times
|
||||||
|
Timetrack is a web application to track my work times for my daily reporting.
|
17
debian/timetrack/DEBIAN/postinst
vendored
Executable file
17
debian/timetrack/DEBIAN/postinst
vendored
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
mkdir -p /var/run/timetrack
|
||||||
|
groupadd timetrack || true
|
||||||
|
useradd -r -g timetrack -d /etc/timetrack -s /sbin/nologin timetrack || true
|
||||||
|
chown timetrack:timetrack -R /etc/timetrack || true
|
||||||
|
chown timetrack:timetrack -R /var/run/timetrack || true
|
||||||
|
chown timetrack:timetrack /usr/bin/timetrack || true
|
||||||
|
cd /var/lib
|
||||||
|
|
||||||
|
systemctl daemon-reload || true
|
||||||
|
systemctl enable timetrack || true
|
||||||
|
|
||||||
|
echo "+------------------------------------------------------------------------------+"
|
||||||
|
echo "| configure timetrack in /etc/timetrack.properties; consider a port change... |"
|
||||||
|
echo "| start timetrack by calling sudo systemctl restart timetrack |"
|
||||||
|
echo "+------------------------------------------------------------------------------+"
|
14
debian/timetrack/etc/systemd/system/timetrack.service
vendored
Normal file
14
debian/timetrack/etc/systemd/system/timetrack.service
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Timetrack
|
||||||
|
After=syslog.target network.target
|
||||||
|
Before=httpd.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
User=timetack
|
||||||
|
Group=timetrack
|
||||||
|
PIDFile=/var/run/timetrack/timetrack.pid
|
||||||
|
ExecStart=/usr/bin/timetrack
|
||||||
|
StandardOutput=null
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
3
debian/timetrack/usr/bin/timetrack
vendored
Executable file
3
debian/timetrack/usr/bin/timetrack
vendored
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
java -Dspring.config.location=/etc/timetrack.properties -jar /var/lib/timetrack.jar
|
@ -1,50 +0,0 @@
|
|||||||
package de.jooqfaces;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author henkej
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public enum EJooqFacesApplicationScope {
|
|
||||||
/**
|
|
||||||
* jooqFacesUrl
|
|
||||||
*/
|
|
||||||
JOOQ_FACES_URL("jooqFacesUrl"),
|
|
||||||
/**
|
|
||||||
* jooqFacesDriver
|
|
||||||
*/
|
|
||||||
JOOQ_FACES_DRIVER("jooqFacesDriver"),
|
|
||||||
/**
|
|
||||||
* jooqFacesSqldialect
|
|
||||||
*/
|
|
||||||
JOOQ_FACES_SQLDIALECT("jooqFacesSqldialect"),
|
|
||||||
/**
|
|
||||||
* jooqFacesProperties
|
|
||||||
*/
|
|
||||||
JOOQ_FACES_PROPERTIES("jooqFacesProperties"),
|
|
||||||
/**
|
|
||||||
* jooqFacesConnectionPool
|
|
||||||
*/
|
|
||||||
JOOQ_FACES_CONNECTIONPOOL("jooqFacesConnectionPool"),
|
|
||||||
/**
|
|
||||||
* jooqFacesMaxPoolSize
|
|
||||||
*/
|
|
||||||
JOOQ_FACES_MAXPOOLSIZE("jooqFacesMaxPoolSize"),
|
|
||||||
/**
|
|
||||||
* jooqFacesParamAutocommit
|
|
||||||
*/
|
|
||||||
JOOQ_FACES_PARAM_AUTOCOMMIT("jooqFacesParamAutocommit");
|
|
||||||
|
|
||||||
private final String s;
|
|
||||||
|
|
||||||
private EJooqFacesApplicationScope(String s) {
|
|
||||||
this.s = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the value
|
|
||||||
*/
|
|
||||||
public final String get() {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package de.jooqfaces;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author jotty
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public enum EJooqFacesConnectionPool {
|
|
||||||
CP_HIKARI("hikari");
|
|
||||||
|
|
||||||
private final String value;
|
|
||||||
|
|
||||||
private EJooqFacesConnectionPool(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String get() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String getHikari() {
|
|
||||||
return CP_HIKARI.get();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package de.jooqfaces;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author henkej
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public enum EJooqFacesSessionScope {
|
|
||||||
CONNECTION("connection");
|
|
||||||
|
|
||||||
private final String value;
|
|
||||||
|
|
||||||
private EJooqFacesSessionScope(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String get() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,350 +0,0 @@
|
|||||||
package de.jooqfaces;
|
|
||||||
|
|
||||||
import java.sql.*;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.faces.application.*;
|
|
||||||
import javax.faces.application.FacesMessage.*;
|
|
||||||
import javax.faces.component.*;
|
|
||||||
import javax.faces.context.*;
|
|
||||||
import javax.faces.render.*;
|
|
||||||
import javax.servlet.*;
|
|
||||||
import javax.sql.*;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.*;
|
|
||||||
import org.jooq.*;
|
|
||||||
import org.jooq.impl.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author jotty
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class JooqFacesContext extends FacesContext {
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger(JooqFacesContext.class);
|
|
||||||
private FacesContext facesContext;
|
|
||||||
private Connection connection;
|
|
||||||
|
|
||||||
public JooqFacesContext(FacesContext facesContext) {
|
|
||||||
this.facesContext = facesContext;
|
|
||||||
setCurrentInstance(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the jooq dsl context from the faces context session map<br />
|
|
||||||
* <br />
|
|
||||||
* <b>Always</b> call getJooq() within a <b>try-catch closure</b>, as the DSLContext is a closure; if not, your
|
|
||||||
* connections might run out
|
|
||||||
*
|
|
||||||
* @return the jooq context
|
|
||||||
* @throws ClassNotFoundException
|
|
||||||
* on driver errors; check if you have attached the correct jdbc driver class
|
|
||||||
* @throws SQLException
|
|
||||||
* on sql errors
|
|
||||||
*/
|
|
||||||
public CloseableDSLContext getJooq() throws ClassNotFoundException, SQLException {
|
|
||||||
ExternalContext externalContext = facesContext.getExternalContext();
|
|
||||||
if (externalContext == null) {
|
|
||||||
throw new JooqFacesException("external context of current faces context is null");
|
|
||||||
}
|
|
||||||
ServletContext servletContext = (ServletContext) externalContext.getContext();
|
|
||||||
if (servletContext == null) {
|
|
||||||
throw new JooqFacesException("servlet context of current faces context is null");
|
|
||||||
}
|
|
||||||
SQLDialect dialect = getSqlDialect(servletContext);
|
|
||||||
createConnectionIfNull(externalContext, servletContext);
|
|
||||||
return new DefaultCloseableDSLContext(new DefaultConnectionProvider(connection), dialect);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the database connection from the session map; if not found, create a new one and add it to the session map
|
|
||||||
*
|
|
||||||
* @param sessionMap
|
|
||||||
* the session map
|
|
||||||
* @param externalContext
|
|
||||||
* the external context
|
|
||||||
* @param servletContext
|
|
||||||
* the servlet context
|
|
||||||
* @return the connection
|
|
||||||
* @throws ClassNotFoundException
|
|
||||||
* on driver errors (e.g. missing jdbc lib)
|
|
||||||
* @throws SQLException
|
|
||||||
* on sql errors
|
|
||||||
*/
|
|
||||||
private void createConnectionIfNull(ExternalContext externalContext, ServletContext servletContext)
|
|
||||||
throws ClassNotFoundException, SQLException {
|
|
||||||
if (connection == null) { // caching the connection within the faces context makes it faster on the jsf life cycle
|
|
||||||
Map<String, Object> sessionMap = externalContext.getSessionMap();
|
|
||||||
if (sessionMap == null) {
|
|
||||||
throw new JooqFacesException("session map of current faces context is null");
|
|
||||||
}
|
|
||||||
DataSource dataSource = (DataSource) sessionMap.get(EJooqFacesSessionScope.CONNECTION.get());
|
|
||||||
if (dataSource == null || dataSource.getConnection() == null || dataSource.getConnection().isClosed()) {
|
|
||||||
LOGGER.debug("creating new connection pool");
|
|
||||||
dataSource = getDataSourceFromServletContext(servletContext);
|
|
||||||
externalContext.getSessionMap().put(EJooqFacesSessionScope.CONNECTION.get(), dataSource);
|
|
||||||
}
|
|
||||||
connection = dataSource.getConnection();
|
|
||||||
String autoCommit = servletContext.getInitParameter(EJooqFacesApplicationScope.JOOQ_FACES_PARAM_AUTOCOMMIT.get());
|
|
||||||
connection.setAutoCommit("true".equals(autoCommit)); // default false for postgreSQL, the database of my choice
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get data source from connection pool if defined in servlet context (see
|
|
||||||
* EJooqFacesApplicationScope.CONNECTION_POOL); if not defined, return a plain data source
|
|
||||||
*
|
|
||||||
* @param servletContext
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static final DataSource getDataSourceFromServletContext(ServletContext servletContext)
|
|
||||||
throws ClassNotFoundException {
|
|
||||||
String driver = getDriver(servletContext);
|
|
||||||
if (driver == null) {
|
|
||||||
throw new JooqFacesException(
|
|
||||||
"undefined driver in application scope, define it in your web.xml's context-param on name "
|
|
||||||
+ EJooqFacesApplicationScope.JOOQ_FACES_DRIVER.get());
|
|
||||||
}
|
|
||||||
String url = getUrl(servletContext);
|
|
||||||
if (url == null) {
|
|
||||||
throw new JooqFacesException(
|
|
||||||
"undefined connection data url in application scope, define it in your web.xml's context-param on name "
|
|
||||||
+ EJooqFacesApplicationScope.JOOQ_FACES_URL.get());
|
|
||||||
}
|
|
||||||
Integer maxPoolSize = getMaxPoolSize(servletContext);
|
|
||||||
if (maxPoolSize == null) {
|
|
||||||
LOGGER.debug("maxPoolSize not set, setting it to 20");
|
|
||||||
maxPoolSize = 20;
|
|
||||||
}
|
|
||||||
String connectionPool = getConnectionPool(servletContext);
|
|
||||||
if (connectionPool == null) {
|
|
||||||
LOGGER.warn(
|
|
||||||
"no connection pool set in servlet context (see EJooqFacesApplicationScope.JOOQ_FACES_CONNECTIONPOOL), using plain connection");
|
|
||||||
}
|
|
||||||
return new PoollessDataSource(driver, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the connection from the servlet context
|
|
||||||
*
|
|
||||||
* @param servletContext
|
|
||||||
* the servlet context
|
|
||||||
* @return the connection
|
|
||||||
* @throws ClassNotFoundException
|
|
||||||
* on driver errors (e.g. missing jdbc lib)
|
|
||||||
* @throws SQLException
|
|
||||||
* on sql errors
|
|
||||||
*/
|
|
||||||
private static final Connection getConnectionFromServletContext(ServletContext servletContext)
|
|
||||||
throws ClassNotFoundException, SQLException {
|
|
||||||
DataSource dataSource = getDataSourceFromServletContext(servletContext);
|
|
||||||
return dataSource.getConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get a jooq connection from servlet context (for such cases as the deployment phase where the faces context is still
|
|
||||||
* not available)
|
|
||||||
*
|
|
||||||
* @param servletContext
|
|
||||||
* the servlet context
|
|
||||||
* @return a jooq connection
|
|
||||||
* @throws ClassNotFoundException
|
|
||||||
* on driver errors (e.g. missing jdbc lib)
|
|
||||||
* @throws SQLException
|
|
||||||
* on sql errors
|
|
||||||
*/
|
|
||||||
public static final DSLContext getJooqFromServletContext(ServletContext servletContext)
|
|
||||||
throws ClassNotFoundException, SQLException {
|
|
||||||
SQLDialect dialect = getSqlDialect(servletContext);
|
|
||||||
Connection con = getConnectionFromServletContext(servletContext);
|
|
||||||
return DSL.using(con, dialect);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addMessage(String clientId, FacesMessage message) {
|
|
||||||
facesContext.addMessage(clientId, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Application getApplication() {
|
|
||||||
return facesContext.getApplication();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<String> getClientIdsWithMessages() {
|
|
||||||
return facesContext.getClientIdsWithMessages();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ExternalContext getExternalContext() {
|
|
||||||
return facesContext.getExternalContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Severity getMaximumSeverity() {
|
|
||||||
return facesContext.getMaximumSeverity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<FacesMessage> getMessages() {
|
|
||||||
return facesContext.getMessages();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<FacesMessage> getMessages(String clientId) {
|
|
||||||
return facesContext.getMessages(clientId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RenderKit getRenderKit() {
|
|
||||||
return facesContext.getRenderKit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean getRenderResponse() {
|
|
||||||
return facesContext.getRenderResponse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean getResponseComplete() {
|
|
||||||
|
|
||||||
return facesContext.getResponseComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResponseStream getResponseStream() {
|
|
||||||
return facesContext.getResponseStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResponseWriter getResponseWriter() {
|
|
||||||
return facesContext.getResponseWriter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UIViewRoot getViewRoot() {
|
|
||||||
return facesContext.getViewRoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void release() {
|
|
||||||
facesContext.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void renderResponse() {
|
|
||||||
facesContext.renderResponse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void responseComplete() {
|
|
||||||
facesContext.responseComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setResponseStream(ResponseStream responseStream) {
|
|
||||||
facesContext.setResponseStream(responseStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setResponseWriter(ResponseWriter responseWriter) {
|
|
||||||
facesContext.setResponseWriter(responseWriter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setViewRoot(UIViewRoot root) {
|
|
||||||
facesContext.setViewRoot(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the connection pool from initial context
|
|
||||||
*
|
|
||||||
* @param servletContext
|
|
||||||
* the context
|
|
||||||
* @return the connection pool string or null
|
|
||||||
*/
|
|
||||||
private static final String getConnectionPool(ServletContext servletContext) {
|
|
||||||
return servletContext.getInitParameter(EJooqFacesApplicationScope.JOOQ_FACES_CONNECTIONPOOL.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the max pool size from initial context if any
|
|
||||||
*
|
|
||||||
* @param servletContext
|
|
||||||
* the context of this function call
|
|
||||||
* @return the max pool size or null
|
|
||||||
*/
|
|
||||||
private static final Integer getMaxPoolSize(ServletContext servletContext) {
|
|
||||||
String maxPoolSize = servletContext.getInitParameter(EJooqFacesApplicationScope.JOOQ_FACES_MAXPOOLSIZE.get());
|
|
||||||
return maxPoolSize == null ? null : Integer.valueOf(maxPoolSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get driver from initial context
|
|
||||||
*
|
|
||||||
* @param servletContext
|
|
||||||
* the context of this function call
|
|
||||||
* @return the parameter value of the jooq faces driver
|
|
||||||
*/
|
|
||||||
private static final String getDriver(ServletContext servletContext) {
|
|
||||||
return servletContext.getInitParameter(EJooqFacesApplicationScope.JOOQ_FACES_DRIVER.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get driver connection url from initial context
|
|
||||||
*
|
|
||||||
* @param servletContext
|
|
||||||
* the context of this function call
|
|
||||||
* @return the parameter value of the jooq faces url
|
|
||||||
*/
|
|
||||||
private static final String getUrl(ServletContext servletContext) {
|
|
||||||
return servletContext.getInitParameter(EJooqFacesApplicationScope.JOOQ_FACES_URL.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* find jooq sql dialect class for dialectName
|
|
||||||
*
|
|
||||||
* @param dialectName
|
|
||||||
* name of dialect
|
|
||||||
* @return SQLDialect if found, null otherwise
|
|
||||||
*/
|
|
||||||
private static final SQLDialect findDialect(String dialectName) {
|
|
||||||
if (dialectName == null) {
|
|
||||||
LOGGER.error("Sql dialect name is null");
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
for (SQLDialect dialect : SQLDialect.values()) {
|
|
||||||
LOGGER.trace("Sql dialect comparing: dialectName={}, loopDialect={}", dialectName, dialect);
|
|
||||||
if (dialectName.equalsIgnoreCase(dialect.name())) {
|
|
||||||
LOGGER.debug("Sql dialect found: dialectName={}, foundDialect={}", dialectName, dialect);
|
|
||||||
return dialect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOGGER.error("Sql dialect not found: dialectName={}", dialectName);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get jooq sql dialect from initial context
|
|
||||||
*
|
|
||||||
* @param servletContext
|
|
||||||
* the context of this function call
|
|
||||||
* @return the dialect or null
|
|
||||||
*/
|
|
||||||
private static final SQLDialect getSqlDialect(ServletContext servletContext) {
|
|
||||||
String dialectName = servletContext.getInitParameter(EJooqFacesApplicationScope.JOOQ_FACES_SQLDIALECT.get());
|
|
||||||
return getSqlDialect(dialectName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get sql dialect from name
|
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* the dialect name
|
|
||||||
* @return the dialect or null
|
|
||||||
*/
|
|
||||||
public static final SQLDialect getSqlDialect(String name) {
|
|
||||||
return findDialect(name);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
package de.jooqfaces;
|
|
||||||
|
|
||||||
import javax.faces.*;
|
|
||||||
import javax.faces.context.*;
|
|
||||||
import javax.faces.lifecycle.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author jotty
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class JooqFacesContextFactory extends FacesContextFactory {
|
|
||||||
|
|
||||||
private FacesContextFactory facesContextFactory;
|
|
||||||
|
|
||||||
public JooqFacesContextFactory(FacesContextFactory facesContextFactory) {
|
|
||||||
this.facesContextFactory = facesContextFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FacesContext getFacesContext(Object context, Object request, Object response, Lifecycle lifecycle)
|
|
||||||
throws FacesException {
|
|
||||||
FacesContext facesContext = facesContextFactory.getFacesContext(context, request, response, lifecycle);
|
|
||||||
return new JooqFacesContext(facesContext);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package de.jooqfaces;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author jotty
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class JooqFacesException extends RuntimeException {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
public JooqFacesException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JooqFacesException(Exception e) {
|
|
||||||
super(e);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
package de.jooqfaces;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.sql.*;
|
|
||||||
import java.util.logging.*;
|
|
||||||
|
|
||||||
import javax.sql.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author jotty
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class PoollessDataSource implements DataSource {
|
|
||||||
|
|
||||||
private final String driver;
|
|
||||||
private final String url;
|
|
||||||
|
|
||||||
public PoollessDataSource(String driver, String url) {
|
|
||||||
this.driver = driver;
|
|
||||||
this.url = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PrintWriter getLogWriter() throws SQLException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLoginTimeout() throws SQLException {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLogWriter(PrintWriter out) throws SQLException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLoginTimeout(int seconds) throws SQLException {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isWrapperFor(Class<?> iface) throws SQLException {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T unwrap(Class<T> iface) throws SQLException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Connection getConnection() throws SQLException {
|
|
||||||
try {
|
|
||||||
Class.forName(driver);
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
throw new SQLException(e);
|
|
||||||
}
|
|
||||||
return DriverManager.getConnection(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Connection getConnection(String username, String password) throws SQLException {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
package de.jooqfaces;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.sql.*;
|
|
||||||
import java.util.*;import javax.servlet.*;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author henkej
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class PropertiesDeploymentListener implements ServletContextListener {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger(PropertiesDeploymentListener.class);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contextDestroyed(ServletContextEvent event) {
|
|
||||||
try {
|
|
||||||
Enumeration<Driver> drivers = DriverManager.getDrivers();
|
|
||||||
while (drivers.hasMoreElements()) {
|
|
||||||
DriverManager.deregisterDriver(drivers.nextElement());
|
|
||||||
}
|
|
||||||
} catch (SQLException | SecurityException e) {
|
|
||||||
LOGGER.error("Error deregistering drivers", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contextInitialized(ServletContextEvent event) {
|
|
||||||
try {
|
|
||||||
ServletContext ctx = event.getServletContext();
|
|
||||||
beforeInitialization(ctx);
|
|
||||||
String propertiesFileName = (String) ctx.getInitParameter(EJooqFacesApplicationScope.JOOQ_FACES_PROPERTIES.get());
|
|
||||||
if (propertiesFileName == null) {
|
|
||||||
throw new IOException(
|
|
||||||
"undefined properties file name in application scope, define it in your web.xml's context-param on name "
|
|
||||||
+ EJooqFacesApplicationScope.JOOQ_FACES_PROPERTIES.get());
|
|
||||||
}
|
|
||||||
Properties properties = new Properties();
|
|
||||||
properties.load(new FileInputStream(propertiesFileName));
|
|
||||||
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
|
|
||||||
String key = (String) entry.getKey();
|
|
||||||
String value = (String) entry.getValue();
|
|
||||||
ctx.setInitParameter(key, value);
|
|
||||||
}
|
|
||||||
// ensure to have all needed parameters loaded
|
|
||||||
if (ctx.getInitParameter(EJooqFacesApplicationScope.JOOQ_FACES_SQLDIALECT.get()) == null) {
|
|
||||||
throw new IOException("no " + EJooqFacesApplicationScope.JOOQ_FACES_SQLDIALECT.get()
|
|
||||||
+ " defined in your properties file " + propertiesFileName);
|
|
||||||
}
|
|
||||||
if (ctx.getInitParameter(EJooqFacesApplicationScope.JOOQ_FACES_URL.get()) == null) {
|
|
||||||
throw new IOException("no " + EJooqFacesApplicationScope.JOOQ_FACES_URL.get() + " defined in your properties file "
|
|
||||||
+ propertiesFileName);
|
|
||||||
}
|
|
||||||
if (ctx.getInitParameter(EJooqFacesApplicationScope.JOOQ_FACES_DRIVER.get()) == null) {
|
|
||||||
throw new IOException("no " + EJooqFacesApplicationScope.JOOQ_FACES_DRIVER.get()
|
|
||||||
+ " defined in your properties file " + propertiesFileName);
|
|
||||||
}
|
|
||||||
afterInitialization(ctx);
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.error("Error loading needed parameters from properties file", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* executed directly after initialization if no exception is thrown
|
|
||||||
*
|
|
||||||
* @param ctx
|
|
||||||
* the context to use
|
|
||||||
* @throws IOException
|
|
||||||
* for input output exceptions
|
|
||||||
*/
|
|
||||||
public void afterInitialization(ServletContext ctx) throws IOException {
|
|
||||||
// to be implemented in extending classes
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* executed directly before initialization after getting the context from the servlet
|
|
||||||
*
|
|
||||||
* @param ctx
|
|
||||||
* the context to use
|
|
||||||
* @throws IOException
|
|
||||||
* for input output exceptions
|
|
||||||
*/
|
|
||||||
public void beforeInitialization(ServletContext ctx) throws IOException {
|
|
||||||
// to be implemented in extending classes
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,15 @@
|
|||||||
|
package de.jottyfan.timetrack;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableTransactionManagement
|
||||||
|
public class TimetrackApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(TimetrackApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package de.jottyfan.timetrack.config;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.jooq.SQLDialect;
|
||||||
|
import org.jooq.impl.DataSourceConnectionProvider;
|
||||||
|
import org.jooq.impl.DefaultConfiguration;
|
||||||
|
import org.jooq.impl.DefaultDSLContext;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author henkej
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class InitialConfiguration {
|
||||||
|
@Autowired
|
||||||
|
private DataSource dataSource;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DataSourceConnectionProvider connectionProvider() {
|
||||||
|
return new DataSourceConnectionProvider(new TransactionAwareDataSourceProxy(dataSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DefaultDSLContext dsl() {
|
||||||
|
return new DefaultDSLContext(configuration());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultConfiguration configuration() {
|
||||||
|
DefaultConfiguration jooqConfiguration = new DefaultConfiguration();
|
||||||
|
jooqConfiguration.set(connectionProvider());
|
||||||
|
jooqConfiguration.set(SQLDialect.POSTGRES);
|
||||||
|
// jooqConfiguration.set(new DefaultExecuteListenerProvider(exceptionTransformer()));
|
||||||
|
return jooqConfiguration;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package de.jottyfan.timetrack.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;
|
||||||
|
|
||||||
|
@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().antMatchers("/public/**").permitAll();
|
||||||
|
http.csrf().disable();
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +0,0 @@
|
|||||||
package de.jottyfan.timetrack.help;
|
|
||||||
|
|
||||||
public enum Application
|
|
||||||
{
|
|
||||||
CONNECTION("connection");
|
|
||||||
|
|
||||||
private final String value;
|
|
||||||
|
|
||||||
private Application(String value)
|
|
||||||
{
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String get()
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package de.jottyfan.timetrack.help;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
import javax.servlet.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author henkej
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class EncodingFilter implements Filter
|
|
||||||
{
|
|
||||||
private String encoding;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroy()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException
|
|
||||||
{
|
|
||||||
servletRequest.setCharacterEncoding(encoding);
|
|
||||||
filterChain.doFilter(servletRequest, servletResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(FilterConfig filterConfig) throws ServletException
|
|
||||||
{
|
|
||||||
encoding = filterConfig.getInitParameter("encoding");
|
|
||||||
if (encoding == null)
|
|
||||||
{
|
|
||||||
encoding = "UTF-8";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package de.jottyfan.timetrack.help;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
|
|
||||||
import javax.faces.component.UIComponent;
|
|
||||||
import javax.faces.context.FacesContext;
|
|
||||||
import javax.faces.convert.Converter;
|
|
||||||
import javax.faces.convert.ConverterException;
|
|
||||||
import javax.faces.convert.FacesConverter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author jotty
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@FacesConverter("de.jottyfan.timetrack.help.LocalDateConverter")
|
|
||||||
public class LocalDateConverter implements Converter<LocalDate> {
|
|
||||||
|
|
||||||
protected final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd.MM.yyyy");
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LocalDate getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
|
|
||||||
return LocalDate.from(dtf.parse(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAsString(FacesContext context, UIComponent component, LocalDate value) throws ConverterException {
|
|
||||||
return value.format(dtf);
|
|
||||||
}
|
|
||||||
}
|
|
32
src/main/java/de/jottyfan/timetrack/help/ManifestBean.java
Normal file
32
src/main/java/de/jottyfan/timetrack/help/ManifestBean.java
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package de.jottyfan.timetrack.help;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.info.BuildProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author henkej
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ManifestBean {
|
||||||
|
|
||||||
|
@Value("${server.servlet.context-path:}")
|
||||||
|
private String contextPath;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private BuildProperties buildProperties;
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
buf.append("Version ");
|
||||||
|
buf.append(buildProperties == null ? "?" : buildProperties.getVersion());
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContextPath() {
|
||||||
|
return (contextPath != null && !contextPath.isBlank()) ? String.format("/%s/", contextPath) : "/";
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +0,0 @@
|
|||||||
package de.jottyfan.timetrack.help;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author henkej
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public abstract class Navigation
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* navigate to page referenced by p
|
|
||||||
*
|
|
||||||
* @param p
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public String navigateTo(Pages p)
|
|
||||||
{
|
|
||||||
return p.get();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
package de.jottyfan.timetrack.help;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author henkej
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public enum Pages
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* contact list
|
|
||||||
*/
|
|
||||||
CONTACT_LIST("/pages/contact/list.jsf"),
|
|
||||||
/**
|
|
||||||
* contact item
|
|
||||||
*/
|
|
||||||
CONTACT_ITEM("/pages/contact/item.jsf"),
|
|
||||||
/**
|
|
||||||
* note list
|
|
||||||
*/
|
|
||||||
NOTE_LIST("/pages/note/list.jsf"),
|
|
||||||
/**
|
|
||||||
* note item
|
|
||||||
*/
|
|
||||||
NOTE_ITEM("/pages/note/item.jsf"),
|
|
||||||
/**
|
|
||||||
* todo list
|
|
||||||
*/
|
|
||||||
TODO_LIST("/pages/todo/list.jsf"),
|
|
||||||
/**
|
|
||||||
* todo item
|
|
||||||
*/
|
|
||||||
TODO_ITEM("/pages/todo/item.jsf"),
|
|
||||||
/**
|
|
||||||
* done init
|
|
||||||
*/
|
|
||||||
DONE_INIT("/pages/done/init.jsf"),
|
|
||||||
/**
|
|
||||||
* done edit
|
|
||||||
*/
|
|
||||||
DONE_EDIT("/pages/done/edit.jsf"),
|
|
||||||
/**
|
|
||||||
* done delete
|
|
||||||
*/
|
|
||||||
DONE_DELETE("/pages/done/delete.jsf"),
|
|
||||||
/**
|
|
||||||
* done add
|
|
||||||
*/
|
|
||||||
DONE_ADD("/pages/done/add.jsf"),
|
|
||||||
/**
|
|
||||||
* done clear
|
|
||||||
*/
|
|
||||||
DONE_CLEAR("/pages/done/clear.jsf"),
|
|
||||||
/**
|
|
||||||
* done read
|
|
||||||
*/
|
|
||||||
DONE_READ("/pages/done/read.jsf"),
|
|
||||||
/**
|
|
||||||
* organize init
|
|
||||||
*/
|
|
||||||
ORGANIZE_LIST("/pages/organize/list.jsf"),
|
|
||||||
/**
|
|
||||||
* organize week
|
|
||||||
*/
|
|
||||||
ORGANIZE_WEEK("/pages/organize/week.jsf"),
|
|
||||||
/**
|
|
||||||
* organize delete
|
|
||||||
*/
|
|
||||||
ORGANIZE_DELETE("/pages/organize/delete.jsf"),
|
|
||||||
/**
|
|
||||||
* organize add
|
|
||||||
*/
|
|
||||||
ORGANIZE_ADD("/pages/organize/add.jsf"),
|
|
||||||
/**
|
|
||||||
* organize edit
|
|
||||||
*/
|
|
||||||
ORGANIZE_EDIT("/pages/organize/edit.jsf"),
|
|
||||||
/**
|
|
||||||
* start
|
|
||||||
*/
|
|
||||||
START("/pages/start.jsf");
|
|
||||||
|
|
||||||
private final String value;
|
|
||||||
|
|
||||||
private Pages(String value)
|
|
||||||
{
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String get()
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
package de.jottyfan.timetrack.help;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.enterprise.context.SessionScoped;
|
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author henkej
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Named
|
|
||||||
@SessionScoped
|
|
||||||
public class ThemeBean implements Serializable
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private String currentTheme;
|
|
||||||
|
|
||||||
public List<String> getValidThemes(){
|
|
||||||
List<String> list = new ArrayList<>();
|
|
||||||
list.add("cerulean");
|
|
||||||
list.add("cosmo");
|
|
||||||
list.add("cyborg");
|
|
||||||
list.add("darkly");
|
|
||||||
list.add("default");
|
|
||||||
list.add("flatly");
|
|
||||||
list.add("journal");
|
|
||||||
list.add("lumen");
|
|
||||||
list.add("other");
|
|
||||||
list.add("paper");
|
|
||||||
list.add("patternfly");
|
|
||||||
list.add("readable");
|
|
||||||
list.add("sandstone");
|
|
||||||
list.add("simplex");
|
|
||||||
list.add("slate");
|
|
||||||
list.add("spacelab");
|
|
||||||
list.add("superhero");
|
|
||||||
list.add("united");
|
|
||||||
list.add("yeti");
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCurrentTheme(String currentTheme)
|
|
||||||
{
|
|
||||||
this.currentTheme = currentTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* contains current theme for web.xml's theme chooser
|
|
||||||
*
|
|
||||||
* @return current theme
|
|
||||||
*/
|
|
||||||
public String getCurrentTheme()
|
|
||||||
{
|
|
||||||
return currentTheme == null ? "default" : (getValidThemes().contains(currentTheme) ? currentTheme : "default");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
package de.jottyfan.timetrack.modules.contact;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
|
||||||
import javax.faces.context.FacesContext;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import de.jooqfaces.JooqFacesContext;
|
|
||||||
import de.jottyfan.timetrack.help.Navigation;
|
|
||||||
import de.jottyfan.timetrack.help.Pages;
|
|
||||||
import de.jottyfan.timetrack.modules.ControlInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author jotty
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Named
|
|
||||||
@RequestScoped
|
|
||||||
public class ContactControl extends Navigation implements ControlInterface, Serializable {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@Named("contactModel")
|
|
||||||
private ContactModel model;
|
|
||||||
|
|
||||||
public String toStart() {
|
|
||||||
return navigateTo(Pages.START);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toList() {
|
|
||||||
boolean ready = model.init((JooqFacesContext) FacesContext.getCurrentInstance());
|
|
||||||
return ready ? navigateTo(Pages.CONTACT_LIST) : toStart();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toItem(ContactBean bean) {
|
|
||||||
model.setBean(bean);
|
|
||||||
return navigateTo(Pages.CONTACT_ITEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toItem() {
|
|
||||||
model.setBean(new ContactBean(null));
|
|
||||||
return navigateTo(Pages.CONTACT_ITEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String doAdd() {
|
|
||||||
boolean ready = model.add((JooqFacesContext) FacesContext.getCurrentInstance());
|
|
||||||
return ready ? toList() : navigateTo(Pages.CONTACT_ITEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String doUpdate() {
|
|
||||||
boolean ready = model.update((JooqFacesContext) FacesContext.getCurrentInstance());
|
|
||||||
return ready ? toList() : navigateTo(Pages.CONTACT_ITEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String doDelete() {
|
|
||||||
boolean ready = model.delete((JooqFacesContext) FacesContext.getCurrentInstance());
|
|
||||||
return ready ? toList() : navigateTo(Pages.CONTACT_ITEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getAmount() {
|
|
||||||
return model.getAmount((JooqFacesContext) FacesContext.getCurrentInstance());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,176 +0,0 @@
|
|||||||
package de.jottyfan.timetrack.modules.contact;
|
|
||||||
|
|
||||||
import static de.jottyfan.timetrack.db.contact.Tables.T_CONTACT;
|
|
||||||
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.jooq.CloseableDSLContext;
|
|
||||||
import org.jooq.DeleteConditionStep;
|
|
||||||
import org.jooq.InsertValuesStep4;
|
|
||||||
import org.jooq.Record1;
|
|
||||||
import org.jooq.Record5;
|
|
||||||
import org.jooq.SelectJoinStep;
|
|
||||||
import org.jooq.UpdateConditionStep;
|
|
||||||
import org.jooq.exception.DataAccessException;
|
|
||||||
import org.jooq.impl.DSL;
|
|
||||||
|
|
||||||
import de.jooqfaces.JooqFacesContext;
|
|
||||||
import de.jottyfan.timetrack.db.contact.enums.EnumContacttype;
|
|
||||||
import de.jottyfan.timetrack.db.contact.tables.records.TContactRecord;
|
|
||||||
import de.jottyfan.timetrack.modules.JooqGateway;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author jotty
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ContactGateway extends JooqGateway {
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger(ContactGateway.class);
|
|
||||||
|
|
||||||
public ContactGateway(JooqFacesContext facesContext) {
|
|
||||||
super(facesContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get sorted list of contacts
|
|
||||||
*
|
|
||||||
* @return a list (an empty one at least)
|
|
||||||
* @throws SQLException
|
|
||||||
* @throws ClassNotFoundException
|
|
||||||
* @throws DataAccessException
|
|
||||||
*/
|
|
||||||
public List<ContactBean> getAll() throws DataAccessException, ClassNotFoundException, SQLException {
|
|
||||||
try (CloseableDSLContext jooq = getJooq()) {
|
|
||||||
SelectJoinStep<Record5<Integer, String, String, String, EnumContacttype>> sql = jooq
|
|
||||||
// @formatter:off
|
|
||||||
.select(T_CONTACT.PK,
|
|
||||||
T_CONTACT.FORENAME,
|
|
||||||
T_CONTACT.SURNAME,
|
|
||||||
T_CONTACT.CONTACT,
|
|
||||||
T_CONTACT.TYPE)
|
|
||||||
.from(T_CONTACT);
|
|
||||||
// @formatter:on
|
|
||||||
LOGGER.debug("{}", sql.toString());
|
|
||||||
List<ContactBean> list = new ArrayList<>();
|
|
||||||
for (Record5<Integer, String, String, String, EnumContacttype> r : sql.fetch()) {
|
|
||||||
ContactBean bean = new ContactBean(r.get(T_CONTACT.PK));
|
|
||||||
bean.setForename(r.get(T_CONTACT.FORENAME));
|
|
||||||
bean.setSurname(r.get(T_CONTACT.SURNAME));
|
|
||||||
bean.setContact(r.get(T_CONTACT.CONTACT));
|
|
||||||
bean.setType(r.get(T_CONTACT.TYPE));
|
|
||||||
list.add(bean);
|
|
||||||
}
|
|
||||||
list.sort((o1, o2) -> o1 == null ? 0 : o1.compareTo(o2));
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* delete a contact from the database
|
|
||||||
*
|
|
||||||
* @param pk
|
|
||||||
* the id of the contact
|
|
||||||
* @return the number of affected database rows, should be 1
|
|
||||||
* @throws SQLException
|
|
||||||
* @throws ClassNotFoundException
|
|
||||||
* @throws DataAccessException
|
|
||||||
*/
|
|
||||||
public Integer delete(Integer pk) throws DataAccessException, ClassNotFoundException, SQLException {
|
|
||||||
try (CloseableDSLContext jooq = getJooq()) {
|
|
||||||
DeleteConditionStep<TContactRecord> sql = jooq
|
|
||||||
// @formatter:off
|
|
||||||
.deleteFrom(T_CONTACT)
|
|
||||||
.where(T_CONTACT.PK.eq(pk));
|
|
||||||
// @formatter:on
|
|
||||||
LOGGER.debug("{}", sql.toString());
|
|
||||||
return sql.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* add a contact to the database
|
|
||||||
*
|
|
||||||
* @param bean
|
|
||||||
* the contact information
|
|
||||||
* @return the number of affected database rows, should be 1
|
|
||||||
* @throws SQLException
|
|
||||||
* @throws ClassNotFoundException
|
|
||||||
* @throws DataAccessException
|
|
||||||
*/
|
|
||||||
public Integer add(ContactBean bean) throws DataAccessException, ClassNotFoundException, SQLException {
|
|
||||||
try (CloseableDSLContext jooq = getJooq()) {
|
|
||||||
InsertValuesStep4<TContactRecord, String, String, String, EnumContacttype> sql = jooq
|
|
||||||
// @formatter:off
|
|
||||||
.insertInto(T_CONTACT,
|
|
||||||
T_CONTACT.FORENAME,
|
|
||||||
T_CONTACT.SURNAME,
|
|
||||||
T_CONTACT.CONTACT,
|
|
||||||
T_CONTACT.TYPE)
|
|
||||||
.values(bean.getForename(), bean.getSurname(), bean.getContact(), bean.getType());
|
|
||||||
// @formatter:on
|
|
||||||
LOGGER.debug("{}", sql.toString());
|
|
||||||
return sql.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* update a contact in the database, referencing its pk
|
|
||||||
*
|
|
||||||
* @param bean
|
|
||||||
* the contact information
|
|
||||||
* @return the number of affected database rows, should be 1
|
|
||||||
* @throws SQLException
|
|
||||||
* @throws ClassNotFoundException
|
|
||||||
* @throws DataAccessException
|
|
||||||
*/
|
|
||||||
public Integer update(ContactBean bean) throws DataAccessException, ClassNotFoundException, SQLException {
|
|
||||||
try (CloseableDSLContext jooq = getJooq()) {
|
|
||||||
UpdateConditionStep<TContactRecord> sql = jooq
|
|
||||||
// @formatter:off
|
|
||||||
.update(T_CONTACT)
|
|
||||||
.set(T_CONTACT.FORENAME, bean.getForename())
|
|
||||||
.set(T_CONTACT.SURNAME, bean.getSurname())
|
|
||||||
.set(T_CONTACT.CONTACT, bean.getContact())
|
|
||||||
.set(T_CONTACT.TYPE, bean.getType())
|
|
||||||
.where(T_CONTACT.PK.eq(bean.getPk()));
|
|
||||||
// @formatter:on
|
|
||||||
LOGGER.debug("{}", sql.toString());
|
|
||||||
return sql.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get number of entries in t_contact
|
|
||||||
*
|
|
||||||
* @return number of entries
|
|
||||||
* @throws SQLException
|
|
||||||
* @throws ClassNotFoundException
|
|
||||||
* @throws DataAccessException
|
|
||||||
*/
|
|
||||||
public Integer getAmount() throws DataAccessException, ClassNotFoundException, SQLException {
|
|
||||||
try (CloseableDSLContext jooq = getJooq()) {
|
|
||||||
SelectJoinStep<Record1<Integer>> sql = jooq
|
|
||||||
// @formatter:off
|
|
||||||
.selectCount()
|
|
||||||
.from(T_CONTACT);
|
|
||||||
// @formatter:on
|
|
||||||
LOGGER.debug("{}", sql.toString());
|
|
||||||
return sql.fetchOne(DSL.count());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get all enum types
|
|
||||||
*
|
|
||||||
* @return list of enum types
|
|
||||||
*/
|
|
||||||
public List<EnumContacttype> getTypes() {
|
|
||||||
return new ArrayList<>(Arrays.asList(EnumContacttype.values()));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,111 +0,0 @@
|
|||||||
package de.jottyfan.timetrack.modules.contact;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.enterprise.context.SessionScoped;
|
|
||||||
import javax.faces.application.FacesMessage;
|
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import org.jooq.exception.DataAccessException;
|
|
||||||
|
|
||||||
import de.jooqfaces.JooqFacesContext;
|
|
||||||
import de.jottyfan.timetrack.db.contact.enums.EnumContacttype;
|
|
||||||
import de.jottyfan.timetrack.modules.Model;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author jotty
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Named
|
|
||||||
@SessionScoped
|
|
||||||
public class ContactModel implements Model, Serializable {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private ContactBean bean;
|
|
||||||
private List<ContactBean> list;
|
|
||||||
private List<EnumContacttype> types;
|
|
||||||
|
|
||||||
public boolean init(JooqFacesContext facesContext) {
|
|
||||||
bean = new ContactBean(null);
|
|
||||||
try {
|
|
||||||
ContactGateway gw = new ContactGateway(facesContext);
|
|
||||||
list = gw.getAll();
|
|
||||||
types = gw.getTypes();
|
|
||||||
return true;
|
|
||||||
} catch (DataAccessException | ClassNotFoundException | SQLException e) {
|
|
||||||
facesContext.addMessage(null,
|
|
||||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "error on loading data from db", e.getMessage()));
|
|
||||||
list = new ArrayList<>();
|
|
||||||
types = new ArrayList<>();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean delete(JooqFacesContext facesContext) {
|
|
||||||
try {
|
|
||||||
Integer affected = new ContactGateway(facesContext).delete(bean.getPk());
|
|
||||||
return affected.equals(1);
|
|
||||||
} catch (DataAccessException | ClassNotFoundException | SQLException e) {
|
|
||||||
facesContext.addMessage(null,
|
|
||||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "error on deleting data from db", e.getMessage()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean add(JooqFacesContext facesContext) {
|
|
||||||
try {
|
|
||||||
Integer affected = new ContactGateway(facesContext).add(bean);
|
|
||||||
return affected.equals(1);
|
|
||||||
} catch (DataAccessException | ClassNotFoundException | SQLException e) {
|
|
||||||
facesContext.addMessage(null,
|
|
||||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "error on adding data to db", e.getMessage()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean update(JooqFacesContext facesContext) {
|
|
||||||
try {
|
|
||||||
Integer affected = new ContactGateway(facesContext).update(bean);
|
|
||||||
return affected.equals(1);
|
|
||||||
} catch (DataAccessException | ClassNotFoundException | SQLException e) {
|
|
||||||
facesContext.addMessage(null,
|
|
||||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "error on updating data to db", e.getMessage()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getAmount(JooqFacesContext facesContext) {
|
|
||||||
try {
|
|
||||||
return new ContactGateway(facesContext).getAmount();
|
|
||||||
} catch (DataAccessException | ClassNotFoundException | SQLException e) {
|
|
||||||
facesContext.addMessage(null,
|
|
||||||
new FacesMessage(FacesMessage.SEVERITY_ERROR, "error on getting size of contacts", e.getMessage()));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ContactBean> getList() {
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBean(ContactBean bean) {
|
|
||||||
this.bean = bean;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ContactBean getBean() {
|
|
||||||
return bean;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<EnumContacttype> getTypes() {
|
|
||||||
return types;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTypes(List<EnumContacttype> types) {
|
|
||||||
this.types = types;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +1,15 @@
|
|||||||
package de.jottyfan.timetrack.modules.contact;
|
package de.jottyfan.timetrack.spring.contact;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import de.jottyfan.timetrack.db.contact.enums.EnumContacttype;
|
import de.jottyfan.timetrack.db.contact.enums.EnumContacttype;
|
||||||
import de.jottyfan.timetrack.modules.Bean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author jotty
|
* @author jotty
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class ContactBean implements Bean, Serializable, Comparable<ContactBean> {
|
public class ContactBean implements Serializable, Comparable<ContactBean> {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final Integer pk;
|
private final Integer pk;
|
@ -0,0 +1,90 @@
|
|||||||
|
package de.jottyfan.timetrack.spring.contact;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.security.RolesAllowed;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author henkej
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Controller
|
||||||
|
public class ContactController {
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(ContactController.class);
|
||||||
|
|
||||||
|
private final HttpServletRequest request;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IContactService contactService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public ContactController(HttpServletRequest request) {
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/logout")
|
||||||
|
public String getLogout(HttpServletRequest request) throws ServletException {
|
||||||
|
request.logout();
|
||||||
|
return "redirect:/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/")
|
||||||
|
public String getIndex() {
|
||||||
|
return "public/index";
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModelAttribute("currentUser")
|
||||||
|
@ResponseBody
|
||||||
|
public String getCurrentUser() {
|
||||||
|
return contactService.getCurrentUser(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RolesAllowed("timetrack_user")
|
||||||
|
@RequestMapping(value = "/contact/contacts")
|
||||||
|
public String getList(Model model, @ModelAttribute ContactBean bean) {
|
||||||
|
List<ContactBean> list = contactService.toList();
|
||||||
|
model.addAttribute("contactList", list);
|
||||||
|
return "contact/contacts";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RolesAllowed("timetrack_user")
|
||||||
|
@RequestMapping(value = "/contact/add/")
|
||||||
|
public String toAdd(Model model) {
|
||||||
|
return "contact/item";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RolesAllowed("timetrack_user")
|
||||||
|
@RequestMapping(value = "/contact/insert", method = RequestMethod.POST)
|
||||||
|
public String doInsert(Model model, @ModelAttribute ContactBean bean) {
|
||||||
|
Integer amount = contactService.doInsert(bean);
|
||||||
|
return amount.equals(1) ? "contact/contacts" : "concact/item";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RolesAllowed("timetrack_user")
|
||||||
|
@GetMapping("/contact/edit/{id}")
|
||||||
|
public String toEdit(@PathVariable Integer id, Model model) {
|
||||||
|
return "not yet implemented";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RolesAllowed("timetrack_user")
|
||||||
|
@RequestMapping(value = "/contact/delete/")
|
||||||
|
public String doDelete(Model model, @ModelAttribute ContactBean bean) {
|
||||||
|
Integer amount = contactService.doDelete(bean.getPk());
|
||||||
|
return amount.equals(1) ? "contact/contacts" : "contact/contacts";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package de.jottyfan.timetrack.spring.contact;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author henkej
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface IContactService {
|
||||||
|
public List<ContactBean> toList();
|
||||||
|
public Integer doInsert(ContactBean bean);
|
||||||
|
public Integer doUdate(ContactBean bean);
|
||||||
|
public Integer doDelete(Integer pk);
|
||||||
|
public Integer getAmount();
|
||||||
|
public String getCurrentUser(HttpServletRequest request);
|
||||||
|
}
|
@ -0,0 +1,169 @@
|
|||||||
|
package de.jottyfan.timetrack.spring.contact.impl;
|
||||||
|
|
||||||
|
import static de.jottyfan.timetrack.db.contact.Tables.T_CONTACT;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.jooq.DSLContext;
|
||||||
|
import org.jooq.DeleteConditionStep;
|
||||||
|
import org.jooq.InsertValuesStep4;
|
||||||
|
import org.jooq.Record1;
|
||||||
|
import org.jooq.Record5;
|
||||||
|
import org.jooq.SelectJoinStep;
|
||||||
|
import org.jooq.UpdateConditionStep;
|
||||||
|
import org.jooq.exception.DataAccessException;
|
||||||
|
import org.jooq.impl.DSL;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import de.jottyfan.timetrack.db.contact.enums.EnumContacttype;
|
||||||
|
import de.jottyfan.timetrack.db.contact.tables.records.TContactRecord;
|
||||||
|
import de.jottyfan.timetrack.spring.contact.ContactBean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author jotty
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
public class ContactGateway {
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(ContactGateway.class);
|
||||||
|
private final DSLContext jooq;
|
||||||
|
|
||||||
|
public ContactGateway(@Autowired DSLContext jooq) throws Exception {
|
||||||
|
this.jooq = jooq;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DSLContext getJooq() {
|
||||||
|
return this.jooq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get sorted list of contacts
|
||||||
|
*
|
||||||
|
* @return a list (an empty one at least)
|
||||||
|
* @throws SQLException
|
||||||
|
* @throws ClassNotFoundException
|
||||||
|
* @throws DataAccessException
|
||||||
|
*/
|
||||||
|
public List<ContactBean> getAll() throws DataAccessException, ClassNotFoundException, SQLException {
|
||||||
|
SelectJoinStep<Record5<Integer, String, String, String, EnumContacttype>> sql = getJooq()
|
||||||
|
// @formatter:off
|
||||||
|
.select(T_CONTACT.PK,
|
||||||
|
T_CONTACT.FORENAME,
|
||||||
|
T_CONTACT.SURNAME,
|
||||||
|
T_CONTACT.CONTACT,
|
||||||
|
T_CONTACT.TYPE)
|
||||||
|
.from(T_CONTACT);
|
||||||
|
// @formatter:on
|
||||||
|
LOGGER.debug("{}", sql.toString());
|
||||||
|
List<ContactBean> list = new ArrayList<>();
|
||||||
|
for (Record5<Integer, String, String, String, EnumContacttype> r : sql.fetch()) {
|
||||||
|
ContactBean bean = new ContactBean(r.get(T_CONTACT.PK));
|
||||||
|
bean.setForename(r.get(T_CONTACT.FORENAME));
|
||||||
|
bean.setSurname(r.get(T_CONTACT.SURNAME));
|
||||||
|
bean.setContact(r.get(T_CONTACT.CONTACT));
|
||||||
|
bean.setType(r.get(T_CONTACT.TYPE));
|
||||||
|
list.add(bean);
|
||||||
|
}
|
||||||
|
list.sort((o1, o2) -> o1 == null ? 0 : o1.compareTo(o2));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete a contact from the database
|
||||||
|
*
|
||||||
|
* @param pk the id of the contact
|
||||||
|
* @return the number of affected database rows, should be 1
|
||||||
|
* @throws SQLException
|
||||||
|
* @throws ClassNotFoundException
|
||||||
|
* @throws DataAccessException
|
||||||
|
*/
|
||||||
|
public Integer delete(Integer pk) throws DataAccessException, ClassNotFoundException, SQLException {
|
||||||
|
DeleteConditionStep<TContactRecord> sql = getJooq()
|
||||||
|
// @formatter:off
|
||||||
|
.deleteFrom(T_CONTACT)
|
||||||
|
.where(T_CONTACT.PK.eq(pk));
|
||||||
|
// @formatter:on
|
||||||
|
LOGGER.debug("{}", sql.toString());
|
||||||
|
return sql.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add a contact to the database
|
||||||
|
*
|
||||||
|
* @param bean the contact information
|
||||||
|
* @return the number of affected database rows, should be 1
|
||||||
|
* @throws SQLException
|
||||||
|
* @throws ClassNotFoundException
|
||||||
|
* @throws DataAccessException
|
||||||
|
*/
|
||||||
|
public Integer add(ContactBean bean) throws DataAccessException, ClassNotFoundException, SQLException {
|
||||||
|
InsertValuesStep4<TContactRecord, String, String, String, EnumContacttype> sql = getJooq()
|
||||||
|
// @formatter:off
|
||||||
|
.insertInto(T_CONTACT,
|
||||||
|
T_CONTACT.FORENAME,
|
||||||
|
T_CONTACT.SURNAME,
|
||||||
|
T_CONTACT.CONTACT,
|
||||||
|
T_CONTACT.TYPE)
|
||||||
|
.values(bean.getForename(), bean.getSurname(), bean.getContact(), bean.getType());
|
||||||
|
// @formatter:on
|
||||||
|
LOGGER.debug("{}", sql.toString());
|
||||||
|
return sql.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update a contact in the database, referencing its pk
|
||||||
|
*
|
||||||
|
* @param bean the contact information
|
||||||
|
* @return the number of affected database rows, should be 1
|
||||||
|
* @throws SQLException
|
||||||
|
* @throws ClassNotFoundException
|
||||||
|
* @throws DataAccessException
|
||||||
|
*/
|
||||||
|
public Integer update(ContactBean bean) throws DataAccessException, ClassNotFoundException, SQLException {
|
||||||
|
UpdateConditionStep<TContactRecord> sql = getJooq()
|
||||||
|
// @formatter:off
|
||||||
|
.update(T_CONTACT)
|
||||||
|
.set(T_CONTACT.FORENAME, bean.getForename())
|
||||||
|
.set(T_CONTACT.SURNAME, bean.getSurname())
|
||||||
|
.set(T_CONTACT.CONTACT, bean.getContact())
|
||||||
|
.set(T_CONTACT.TYPE, bean.getType())
|
||||||
|
.where(T_CONTACT.PK.eq(bean.getPk()));
|
||||||
|
// @formatter:on
|
||||||
|
LOGGER.debug("{}", sql.toString());
|
||||||
|
return sql.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get number of entries in t_contact
|
||||||
|
*
|
||||||
|
* @return number of entries
|
||||||
|
* @throws SQLException
|
||||||
|
* @throws ClassNotFoundException
|
||||||
|
* @throws DataAccessException
|
||||||
|
*/
|
||||||
|
public Integer getAmount() throws DataAccessException, ClassNotFoundException, SQLException {
|
||||||
|
SelectJoinStep<Record1<Integer>> sql = getJooq()
|
||||||
|
// @formatter:off
|
||||||
|
.selectCount()
|
||||||
|
.from(T_CONTACT);
|
||||||
|
// @formatter:on
|
||||||
|
LOGGER.debug("{}", sql.toString());
|
||||||
|
return sql.fetchOne(DSL.count());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all enum types
|
||||||
|
*
|
||||||
|
* @return list of enum types
|
||||||
|
*/
|
||||||
|
public List<EnumContacttype> getTypes() {
|
||||||
|
return new ArrayList<>(Arrays.asList(EnumContacttype.values()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package de.jottyfan.timetrack.spring.contact.impl;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.jooq.DSLContext;
|
||||||
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import de.jottyfan.timetrack.spring.contact.ContactBean;
|
||||||
|
import de.jottyfan.timetrack.spring.contact.IContactService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author henkej
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Transactional(transactionManager = "transactionManager")
|
||||||
|
public class ContactService implements IContactService {
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(ContactService.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DSLContext dsl;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCurrentUser(HttpServletRequest request) {
|
||||||
|
KeycloakSecurityContext ksc = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
|
||||||
|
return ksc == null ? "" : ksc.getIdToken().getPreferredUsername();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ContactBean> toList() {
|
||||||
|
try {
|
||||||
|
return new ContactGateway(dsl).getAll();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error(e);
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer doInsert(ContactBean bean) {
|
||||||
|
try {
|
||||||
|
return new ContactGateway(dsl).add(bean);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error(e);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer doUdate(ContactBean bean) {
|
||||||
|
try {
|
||||||
|
return new ContactGateway(dsl).update(bean);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error(e);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer doDelete(Integer pk) {
|
||||||
|
try {
|
||||||
|
return new ContactGateway(dsl).delete(pk);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error(e);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getAmount() {
|
||||||
|
return toList().size();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,181 @@
|
|||||||
|
package de.jottyfan.timetrack.modules;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.security.RolesAllowed;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author henkej
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Controller
|
||||||
|
public class TimetrackController {
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(TimetrackController.class);
|
||||||
|
|
||||||
|
private final HttpServletRequest request;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ITimetrackService fuelService;
|
||||||
|
|
||||||
|
private List<FuelBean> cachedFuels;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public TimetrackController(HttpServletRequest request) {
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/logout")
|
||||||
|
public String getLogout(HttpServletRequest request) throws ServletException {
|
||||||
|
request.logout();
|
||||||
|
return "redirect:/";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RolesAllowed("car_user")
|
||||||
|
@GetMapping("/secure/welcome")
|
||||||
|
public String getWelcome() {
|
||||||
|
return "secure/welcome";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/")
|
||||||
|
public String getIndex() {
|
||||||
|
this.cachedFuels = null;
|
||||||
|
return "public/index";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RolesAllowed("car_user")
|
||||||
|
@GetMapping("/secure/to_table")
|
||||||
|
public String getTable() {
|
||||||
|
this.cachedFuels = null;
|
||||||
|
return "secure/table";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RolesAllowed("car_user")
|
||||||
|
@RequestMapping(value = "/secure/to_bean", method = RequestMethod.GET)
|
||||||
|
public String getBean(Model model) {
|
||||||
|
FuelBean bean = model.containsAttribute("fuelBean") ? (FuelBean) model.getAttribute("fuelBean") : new FuelBean();
|
||||||
|
model.addAttribute("fuelBean", bean);
|
||||||
|
return "secure/bean";
|
||||||
|
}
|
||||||
|
|
||||||
|
@RolesAllowed("car_user")
|
||||||
|
@RequestMapping(value = "/secure/to_existing_bean/{fkFuelBean}", method = RequestMethod.GET)
|
||||||
|
public String getBean(Model model, @PathVariable Integer fkFuelBean) throws Exception {
|
||||||
|
FuelBean bean = getFuelBean(fkFuelBean);
|
||||||
|
model.addAttribute("fuelBean", bean);
|
||||||
|
return "secure/bean";
|
||||||
|
}
|
||||||
|
|
||||||
|
private FuelBean getFuelBean(Integer fkFuelBean) throws Exception {
|
||||||
|
for (FuelBean bean : getFuels()) {
|
||||||
|
if (bean.getPk().equals(fkFuelBean)) {
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception("bean not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
@RolesAllowed("car_user")
|
||||||
|
@RequestMapping(value = "/secure/do_upsert", method = RequestMethod.POST)
|
||||||
|
public String getUpsert(Model model, @ModelAttribute FuelBean fuelBean) {
|
||||||
|
Integer affected = fuelService.upsert(fuelBean);
|
||||||
|
model.addAttribute("fuelBean", fuelBean);
|
||||||
|
LOGGER.info("affected rows: {}", affected);
|
||||||
|
return affected > 0 ? getTable() : getBean(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RolesAllowed("car_user")
|
||||||
|
@GetMapping("/secure/jsonfuels")
|
||||||
|
@ResponseBody
|
||||||
|
public List<FuelBean> getJsonFuels() {
|
||||||
|
List<FuelBean> fuels = fuelService.findAll();
|
||||||
|
return fuels;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModelAttribute("fuels")
|
||||||
|
public List<FuelBean> getFuels() {
|
||||||
|
if (cachedFuels == null || cachedFuels.size() < 1) {
|
||||||
|
cachedFuels = fuelService.findAll();
|
||||||
|
}
|
||||||
|
return cachedFuels;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModelAttribute("currentUser")
|
||||||
|
@ResponseBody
|
||||||
|
public String getCurrentUser() {
|
||||||
|
return fuelService.getCurrentUser(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModelAttribute("averagelkm")
|
||||||
|
@ResponseBody
|
||||||
|
public BigDecimal getAverageLiterPerKm() {
|
||||||
|
List<FuelBean> fuels = getFuels();
|
||||||
|
Integer minMileage = 1000000; // my car won't even reach this milestone :)
|
||||||
|
Integer maxMileage = 0;
|
||||||
|
BigDecimal summedAmount = new BigDecimal(0);
|
||||||
|
for (FuelBean bean : fuels) {
|
||||||
|
summedAmount = summedAmount.add(bean.getAmount());
|
||||||
|
minMileage = bean.getMileage() < minMileage ? bean.getMileage() : minMileage;
|
||||||
|
maxMileage = bean.getMileage() > maxMileage ? bean.getMileage() : maxMileage;
|
||||||
|
}
|
||||||
|
BigDecimal totalMileage = new BigDecimal(maxMileage - minMileage);
|
||||||
|
BigDecimal calculated = totalMileage.intValue() != 0
|
||||||
|
? new BigDecimal((summedAmount.doubleValue() / totalMileage.doubleValue()) * 100d)
|
||||||
|
: new BigDecimal(0);
|
||||||
|
return calculated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModelAttribute("averageel")
|
||||||
|
@ResponseBody
|
||||||
|
public BigDecimal getAverageEuroPerLiter() {
|
||||||
|
List<FuelBean> fuels = getFuels();
|
||||||
|
BigDecimal summedLiter = new BigDecimal(0);
|
||||||
|
BigDecimal summedEuro = new BigDecimal(0);
|
||||||
|
for (FuelBean bean : fuels) {
|
||||||
|
summedLiter = summedLiter.add(bean.getAmount());
|
||||||
|
summedEuro = summedEuro.add(bean.getPrice());
|
||||||
|
}
|
||||||
|
BigDecimal calculated = summedLiter.intValue() != 0
|
||||||
|
? new BigDecimal(summedEuro.doubleValue() / summedLiter.doubleValue())
|
||||||
|
: new BigDecimal(0);
|
||||||
|
return calculated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModelAttribute("chartjsdata")
|
||||||
|
@ResponseBody
|
||||||
|
public List<BigDecimal> getChartjsData() {
|
||||||
|
List<FuelBean> fuels = getFuels();
|
||||||
|
List<BigDecimal> list = new ArrayList<>();
|
||||||
|
for (FuelBean bean : fuels) {
|
||||||
|
list.add(bean.getEuro_per_l());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModelAttribute("chartjslabel")
|
||||||
|
@ResponseBody
|
||||||
|
public List<Integer> getChartjsLabel() {
|
||||||
|
List<FuelBean> fuels = getFuels();
|
||||||
|
List<Integer> list = new ArrayList<>();
|
||||||
|
for (FuelBean bean : fuels) {
|
||||||
|
list.add(bean.getMileage());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
21
src/main/resources/application.properties
Normal file
21
src/main/resources/application.properties
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# jooq
|
||||||
|
spring.datasource.driver-class-name=org.postgresql.Driver
|
||||||
|
spring.datasource.url=jdbc:postgresql://localhost:5432/timetrack
|
||||||
|
spring.datasource.username=timetrack
|
||||||
|
spring.datasource.password=timetrack
|
||||||
|
|
||||||
|
# application
|
||||||
|
server.port = 8083
|
||||||
|
|
||||||
|
server.servlet.context-path=/timetrack
|
||||||
|
|
||||||
|
# keycloak
|
||||||
|
keycloak.auth-server-url = http://localhost:8080/
|
||||||
|
keycloak.realm = jottyfan
|
||||||
|
keycloak.resource = timetrack
|
||||||
|
keycloak.public-client = true
|
||||||
|
keycloak.security-constraints[0].authRoles[0] = timetrack_user
|
||||||
|
keycloak.security-constraints[0].securityCollections[0].patterns[0] = /*
|
||||||
|
#keycloak.credentia
|
||||||
|
keycloak.use-resource-role-mappings=true
|
||||||
|
#keycloak.bearer-only=true
|
@ -1,3 +1,12 @@
|
|||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #eee;
|
||||||
|
height: calc(100% - 76px);
|
||||||
|
}
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -7,7 +16,8 @@
|
|||||||
.page {
|
.page {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-image: linear-gradient(to bottom, #ffffff 10%, #afffff 40%)
|
padding-bottom: 12px;
|
||||||
|
background-image: linear-gradient(to bottom, #eee, #777)
|
||||||
!important;
|
!important;
|
||||||
}
|
}
|
||||||
|
|
31
src/main/resources/templates/contact/contacts.html
Normal file
31
src/main/resources/templates/contact/contacts.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
xmlns:sec="http://www.thymeleaf.org/extras/spring-security" layout:decorate="~{layout/main.html}">
|
||||||
|
<head>
|
||||||
|
<title>Kontakte</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ul layout:fragment="menu">
|
||||||
|
<li class="nav-item" sec:authorize="hasRole('timetrack_user')"><a class="nav-link" th:href="@{/contact/add}">Neuen
|
||||||
|
Kontakt anlegen</a></li>
|
||||||
|
</ul>
|
||||||
|
<main layout:fragment="content">
|
||||||
|
<div class="row row-cols-12 ro-cols-lg-4 ro-cols-md-3 ro-cols-sd-2 g-4" style="margin: 8px">
|
||||||
|
<div class="col" th:each="contact : ${contactList}">
|
||||||
|
<div class="card text-dark bg-light shadow" style="width: 18rem">
|
||||||
|
<div class="card-header text-center">
|
||||||
|
<font th:text="${contact.forename} + ' ' + ${contact.surname}" style="font-size: larger"></font>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex justify-content-center align-items-center">
|
||||||
|
<span th:text="${contact.type} + ': ' + ${contact.contact}"></span> <a th:href="@{/contact/edit/{id}(id=${contact.pk})}"
|
||||||
|
sec:authorize="hasRole('timetrack_user')" style="margin-left: 8px;"> <i class="fa fa-edit"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
34
src/main/resources/templates/contact/item.html
Normal file
34
src/main/resources/templates/contact/item.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
xmlns:sec="http://www.thymeleaf.org/extras/spring-security" layout:decorate="~{layout/main.html}">
|
||||||
|
<head>
|
||||||
|
<title>Kontakt aktualisieren</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ul layout:fragment="menu">
|
||||||
|
<li class="nav-item" sec:authorize="hasRole('timetrack_user')"><a class="nav-link" th:href="@{/contact/contacts}">abbrechen</a></li>
|
||||||
|
</ul>
|
||||||
|
<main layout:fragment="content">
|
||||||
|
<div class="container">
|
||||||
|
<form th:action="@{/contact/insert}" th:object="${contactBean}" method="post">
|
||||||
|
<p>
|
||||||
|
Inhalt von Eintrag <span th:text="*{pk}"></span>:
|
||||||
|
</p>
|
||||||
|
<input type="hidden" th:field="*{pk}" />
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Vorname</label> <input type="text" th:field="*{forename}" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Nachname</label> <input type="text" th:field="*{surname}" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Kontakt</label> <input type="text" th:field="*{contact}" class="form-control" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Typ</label> <input type="text" th:field="*{type}" class="form-control" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
20
src/main/resources/templates/error.html
Normal file
20
src/main/resources/templates/error.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
xmlns:sec="http://www.thymeleaf.org/extras/spring-security" layout:decorate="~{layout/main.html}">
|
||||||
|
<head>
|
||||||
|
<title>Timetrack</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ul layout:fragment="menu">
|
||||||
|
</ul>
|
||||||
|
<main layout:fragment="content">
|
||||||
|
<div style="margin: 10vh">
|
||||||
|
<div class="alert alert-danger" role="alert" style="vertical-align: middle; align: center">
|
||||||
|
<p>Es ist ein Fehler aufgetreten.</p>
|
||||||
|
<a th:href="@{/}" class="alert-link">Ach, Mist...</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
46
src/main/resources/templates/layout/main.html
Normal file
46
src/main/resources/templates/layout/main.html
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" th:href="@{/webjars/bootstrap/5.1.3/css/bootstrap.min.css}" />
|
||||||
|
<link rel="stylesheet" type="text/css" th:href="@{/webjars/datatables/1.11.3/css/dataTables.bootstrap5.min.css}" />
|
||||||
|
<link rel="stylesheet" type="text/css" th:href="@{/webjars/font-awesome/5.15.4/css/all.min.css}">
|
||||||
|
<link rel="stylesheet" type="text/css" th:href="@{/css/style.css}">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" th:href="@{/png/favicon/favicon-32x32.png}">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" th:href="@{/png/favicon/favicon-16x16.png}">
|
||||||
|
|
||||||
|
<script th:src="@{/webjars/jquery/3.6.0/jquery.min.js}"></script>
|
||||||
|
<script th:src="@{/webjars/bootstrap/5.1.3/js/bootstrap.bundle.min.js}"></script>
|
||||||
|
<script th:src="@{/webjars/datatables/1.11.3/js/jquery.dataTables.min.js}"></script>
|
||||||
|
<script th:src="@{/webjars/datatables/1.11.3/js/dataTables.bootstrap5.min.js}"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Navigation -->
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-light bg-light static-top">
|
||||||
|
<div class="container-fluid" style="width: 98%">
|
||||||
|
<a class="navbar-brand" th:href="@{/}"> <i class="fa fa-calendar-alt"></i> Timetrack
|
||||||
|
<div class="version" th:text="${@manifestBean.getVersion()}"></div>
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive"
|
||||||
|
aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||||
|
<ul class="navbar-nav ml-auto">
|
||||||
|
<li class="nav-item dropdown"><a class="nav-link dropdown-toggle" href="#" id="navbarScrollingDropdown" role="button"
|
||||||
|
data-bs-toggle="dropdown" aria-expanded="false"> Module </a>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-light" aria-labelledby="navbarScrollingDropdown">
|
||||||
|
<li><a class="dropdown-item" th:href="@{/contact/contacts}">Kontakte</a></li>
|
||||||
|
<li><hr /></li>
|
||||||
|
<li><a class="dropdown-item" th:href="@{/logout}">[[${currentUser}]] abmelden</a></li>
|
||||||
|
</ul></li>
|
||||||
|
<li layout:fragment="menu" style="list-style-type: none"></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<main layout:fragment="content" class="page body"></main>
|
||||||
|
</body>
|
||||||
|
</html>
|
12
src/main/resources/templates/public/index.html
Normal file
12
src/main/resources/templates/public/index.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
xmlns:sec="http://www.thymeleaf.org/extras/spring-security" layout:decorate="~{layout/main.html}">
|
||||||
|
<head>
|
||||||
|
<title>Timetrack</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ul layout:fragment="menu">
|
||||||
|
</ul>
|
||||||
|
<main layout:fragment="content"></main>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,28 +0,0 @@
|
|||||||
package de.jottyfan.timetrack.help;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author jotty
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class TestLocalDateConverter {
|
|
||||||
@Test
|
|
||||||
public void testGetAsObject() {
|
|
||||||
String now = new SimpleDateFormat("dd.MM.yyyy").format(new Date());
|
|
||||||
assertEquals(LocalDate.now(), new LocalDateConverter().getAsObject(null, null, now));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetAsString() {
|
|
||||||
String now = new SimpleDateFormat("dd.MM.yyyy").format(new Date());
|
|
||||||
assertEquals(now, new LocalDateConverter().getAsString(null, null, LocalDate.now()));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
package de.jottyfan.timetrack.moduls.done;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
|
||||||
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import de.jottyfan.timetrack.modules.done.DoneBean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author henkej
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class TestDoneBean {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetLocalDateTimeFromHHmm() {
|
|
||||||
LocalDateTime ldt = LocalDateTime.now();
|
|
||||||
ldt = ldt.withSecond(0).withNano(0);
|
|
||||||
assertEquals(ldt.withHour(0).withMinute(0), new DoneBean().getLocalDateTimeFromHHmm("0:0", LocalDateTime.now()));
|
|
||||||
assertEquals(ldt.withHour(0).withMinute(5), new DoneBean().getLocalDateTimeFromHHmm("0:5", LocalDateTime.now()));
|
|
||||||
assertEquals(ldt.withHour(3).withMinute(2), new DoneBean().getLocalDateTimeFromHHmm("3:2", LocalDateTime.now()));
|
|
||||||
assertEquals(ldt.withHour(3).withMinute(2), new DoneBean().getLocalDateTimeFromHHmm("03:2", LocalDateTime.now()));
|
|
||||||
assertEquals(ldt.withHour(10).withMinute(2), new DoneBean().getLocalDateTimeFromHHmm("10:2", LocalDateTime.now()));
|
|
||||||
assertEquals(ldt.withHour(12).withMinute(2), new DoneBean().getLocalDateTimeFromHHmm("12:02", LocalDateTime.now()));
|
|
||||||
assertEquals(ldt.withHour(12).withMinute(20), new DoneBean().getLocalDateTimeFromHHmm("12:20", LocalDateTime.now()));
|
|
||||||
assertEquals(null, new DoneBean().getLocalDateTimeFromHHmm(null, LocalDateTime.now()));
|
|
||||||
assertEquals(null, new DoneBean().getLocalDateTimeFromHHmm("", LocalDateTime.now()));
|
|
||||||
assertEquals(null, new DoneBean().getLocalDateTimeFromHHmm(" ", LocalDateTime.now()));
|
|
||||||
assertEquals(ldt.withHour(0).withMinute(0), new DoneBean().getLocalDateTimeFromHHmm("0", LocalDateTime.now()));
|
|
||||||
assertEquals(ldt.withHour(1).withMinute(0), new DoneBean().getLocalDateTimeFromHHmm("1", LocalDateTime.now()));
|
|
||||||
assertEquals(ldt.withHour(5).withMinute(0), new DoneBean().getLocalDateTimeFromHHmm("5", LocalDateTime.now()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetDay() throws ParseException {
|
|
||||||
String today = new SimpleDateFormat("dd.MM.yyyy").format(new Date());
|
|
||||||
DateTimeFormatter sdf = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm");
|
|
||||||
DateTimeFormatter sd = DateTimeFormatter.ofPattern("dd.MM.yyyy");
|
|
||||||
|
|
||||||
DoneBean bean = new DoneBean();
|
|
||||||
bean.setDay(new Date());
|
|
||||||
assertNull(bean.getTimeFrom());
|
|
||||||
assertNull(bean.getTimeUntil());
|
|
||||||
|
|
||||||
bean.setTimeFromString("08:00");
|
|
||||||
LocalDateTime ts1 = bean.getTimeFrom();
|
|
||||||
assertEquals(today.concat(" 08:00"), ts1.format(sdf));
|
|
||||||
bean.setDay(new SimpleDateFormat("dd.MM.yyyy").parse("01.01.2001"));
|
|
||||||
assertEquals("01.01.2001", bean.getTimeFrom().format(sd));
|
|
||||||
assertNull(bean.getTimeUntil());
|
|
||||||
|
|
||||||
bean.setTimeUntilString("10:00");
|
|
||||||
LocalDateTime ts2 = bean.getTimeUntil();
|
|
||||||
assertEquals(today.concat(" 10:00"), ts2.format(sdf));
|
|
||||||
bean.setDay(new SimpleDateFormat("dd.MM.yyyy").parse("01.01.2001"));
|
|
||||||
assertEquals("01.01.2001", sd.format(bean.getTimeFrom()));
|
|
||||||
assertEquals("01.01.2001", sd.format(bean.getTimeUntil()));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package de.jottyfan.timetrack.moduls.done;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import de.jottyfan.timetrack.modules.done.RgbColor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author henkej
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class TestRgbColor {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDetermineRgbColor() {
|
|
||||||
RgbColor c = new RgbColor();
|
|
||||||
assertEquals("rgb(146,204,204)", c.determineRgbColor(null, null, null));
|
|
||||||
assertEquals("rgb(61,66,66)", c.determineRgbColor("Ship", "alles", "Mail"));
|
|
||||||
assertEquals("rgb(43,85,85)", c.determineRgbColor("Square", "Entwicklung_Programmierung", "alles"));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package de.jottyfan.timetrack.moduls.done;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import de.jottyfan.timetrack.modules.done.WholeDaySummaryBean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author henkej
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class TestWholeDaySummaryBean {
|
|
||||||
@Test
|
|
||||||
public void testGetOvertime() {
|
|
||||||
assertEquals("00:00", new WholeDaySummaryBean("", "", "07:48", "", 468).getOvertime());
|
|
||||||
assertEquals("00:12", new WholeDaySummaryBean("", "", "08:00", "", 468).getOvertime());
|
|
||||||
assertEquals("-00:48", new WholeDaySummaryBean("", "", "07:00", "", 468).getOvertime());
|
|
||||||
assertEquals("-07:48", new WholeDaySummaryBean("", "", "00:00", "", 468).getOvertime());
|
|
||||||
assertEquals("-07:48", new WholeDaySummaryBean("", "", "", "", 468).getOvertime());
|
|
||||||
assertEquals("", new WholeDaySummaryBean("", "", "a:b", "", 468).getOvertime());
|
|
||||||
assertEquals("00:00", new WholeDaySummaryBean("", "", "08:00", "", 480).getOvertime());
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user