basic contact overview
This commit is contained in:
15
.classpath
15
.classpath
@ -14,23 +14,16 @@
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="bin/test" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="test" value="true"/>
|
||||
<attribute name="gradle_scope" value="test"/>
|
||||
<attribute name="gradle_used_by_scope" value="test"/>
|
||||
<attribute name="test" value="true"/>
|
||||
</attributes>
|
||||
</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>
|
||||
<attribute name="gradle_scope" value="main"/>
|
||||
<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"/>
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
</classpath>
|
||||
|
10
.project
10
.project
@ -10,6 +10,11 @@
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.common.project.facet.core.builder</name>
|
||||
<arguments>
|
||||
@ -20,11 +25,6 @@
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
|
@ -1,10 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project-modules id="moduleCoreId" project-version="1.5.0">
|
||||
<wb-module deploy-name="timetrack">
|
||||
<property name="context-root" value="timetrack"/>
|
||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/resources"/>
|
||||
<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>
|
||||
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
|
||||
|
||||
<wb-module deploy-name="timetrack">
|
||||
|
||||
<property name="context-root" value="timetrack"/>
|
||||
|
||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/resources"/>
|
||||
|
||||
<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>
|
||||
|
97
build.gradle
97
build.gradle
@ -1,77 +1,70 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
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'
|
||||
}
|
||||
plugins {
|
||||
id 'org.springframework.boot' version '2.6.5'
|
||||
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'war'
|
||||
apply plugin: 'eclipse'
|
||||
group = 'de.jottyfan'
|
||||
version = '1.2.0'
|
||||
sourceCompatibility = '11'
|
||||
|
||||
group = 'jottyfan'
|
||||
version = '1.1.9.1'
|
||||
ext['spring-framework.version'] = '5.3.18'
|
||||
|
||||
description = """timetrack"""
|
||||
|
||||
sourceCompatibility = 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 {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
maven { url "https://www.jottyfan.de/mvnrepo" }
|
||||
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 {
|
||||
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.apache.myfaces.core:myfaces-api:2.3.8'
|
||||
implementation 'org.apache.myfaces.core:myfaces-impl:2.3.8'
|
||||
implementation 'net.bootsfaces:bootsfaces:1.5.0'
|
||||
implementation 'org.postgresql:postgresql:42.2.19'
|
||||
implementation 'org.jooq:jooq:3.14.4'
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
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'
|
||||
implementation 'org.webjars.bowergithub.datatables:datatables:1.10.21'
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.0-M1'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.0-M1'
|
||||
implementation 'org.keycloak:keycloak-spring-boot-starter:17.0.1'
|
||||
|
||||
implementation 'org.postgresql:postgresql:42.2.19'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-jooq'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
|
||||
implementation 'de.jottyfan:timetrackjooq:0.1.0'
|
||||
|
||||
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.0.0'
|
||||
|
||||
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 {
|
||||
archiveName 'timetrack.war'
|
||||
manifest {
|
||||
attributes('Implementation-Version': project.version)
|
||||
}
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
|
||||
// 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 de.jottyfan.timetrack.db.contact.enums.EnumContacttype;
|
||||
import de.jottyfan.timetrack.modules.Bean;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jotty
|
||||
*
|
||||
*/
|
||||
public class ContactBean implements Bean, Serializable, Comparable<ContactBean> {
|
||||
public class ContactBean implements Serializable, Comparable<ContactBean> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
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 {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@ -7,7 +16,8 @@
|
||||
.page {
|
||||
height: 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;
|
||||
}
|
||||
|
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