add java code
This commit is contained in:
parent
6d84626599
commit
671cfdd486
127
build.gradle
Normal file
127
build.gradle
Normal file
@ -0,0 +1,127 @@
|
||||
buildscript {
|
||||
ext {
|
||||
versionSpringBoot = '1.5.10.RELEASE'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath group: 'org.springframework.boot', name: 'spring-boot-gradle-plugin', version: versionSpringBoot
|
||||
}
|
||||
}
|
||||
|
||||
group 'ru.ulstu'
|
||||
version '0.1.0-SNAPSHOT'
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'org.springframework.boot'
|
||||
apply plugin: 'io.spring.dependency-management'
|
||||
apply plugin: 'checkstyle'
|
||||
|
||||
build.dependsOn checkstyleMain
|
||||
bootRun.dependsOn checkstyleMain
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
checkstyle {
|
||||
|
||||
project.ext.checkstyleVersion = '8.8'
|
||||
project.ext.sevntuChecksVersion = '1.28.0'
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
ignoreFailures = false
|
||||
showViolations = true
|
||||
maxErrors = 1
|
||||
maxWarnings = 1
|
||||
configFile = file("${project.rootDir}/checkstyle.xml")
|
||||
|
||||
//sourceSets = [sourceSets.main]
|
||||
//showViolations = true
|
||||
//reportsDir = file("$project.buildDir/checkstyleReports")
|
||||
//configProperties = ['baseDir': "$project.projectDir"]
|
||||
|
||||
//https://discuss.gradle.org/t/some-checkstyle-rules-dont-work-in-gradle/16102/4
|
||||
checkstyleMain {
|
||||
source = sourceSets.main.allSource
|
||||
}
|
||||
|
||||
configurations {
|
||||
checkstyle
|
||||
}
|
||||
|
||||
dependencies{
|
||||
assert project.hasProperty("checkstyleVersion")
|
||||
|
||||
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
||||
checkstyle "com.github.sevntu-checkstyle:sevntu-checks:${sevntuChecksVersion}"
|
||||
}
|
||||
}
|
||||
|
||||
task health(dependsOn: [
|
||||
'checkstyleMain'
|
||||
])
|
||||
|
||||
|
||||
jar {
|
||||
baseName = 'ng-tracker'
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
configurations {
|
||||
compile.exclude module: "spring-boot-starter-tomcat"
|
||||
compile.exclude module: "bcmail-jdk14"
|
||||
compile.exclude module: "bcprov-jdk14"
|
||||
compile.exclude module: "bctsp-jdk14"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web'
|
||||
compile group: 'org.springframework.boot', name: 'spring-boot-starter-security'
|
||||
compile group: 'org.springframework.boot', name: 'spring-boot-starter-aop'
|
||||
compile group: 'org.springframework.boot', name: 'spring-boot-starter-mail'
|
||||
compile group: 'org.springframework.boot', name: 'spring-boot-starter-jetty'
|
||||
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa'
|
||||
compile group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf'
|
||||
compile group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity4'
|
||||
compile group: 'com.fasterxml.jackson.module', name: 'jackson-module-afterburner'
|
||||
compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-hibernate5'
|
||||
|
||||
compile group: 'postgresql', name: 'postgresql', version: '9.1-901.jdbc4'
|
||||
|
||||
compile group: 'org.liquibase', name: 'liquibase-core', version: '3.5.3'
|
||||
compile group: 'com.mattbertolini', name: 'liquibase-slf4j', version: '2.0.0'
|
||||
|
||||
compile group: 'org.apache.poi', name: 'poi', version: '3.9'
|
||||
compile group: 'org.apache.poi', name: 'poi-ooxml', version: '3.9'
|
||||
|
||||
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7'
|
||||
|
||||
compile group: 'com.lowagie', name: 'itext', version: '2.1.7'
|
||||
|
||||
compile group: 'org.webjars', name: 'bootstrap', version: '3.3.7-1'
|
||||
compile group: 'org.webjars', name: 'bootstrap-select', version: '1.12.4'
|
||||
compile group: 'org.webjars', name: 'jquery', version: '3.3.1-1'
|
||||
compile group: 'org.webjars', name: 'font-awesome', version: '4.7.0'
|
||||
compile group: 'org.webjars', name: 'jstree', version: '3.3.3'
|
||||
|
||||
compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.5.0'
|
||||
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.5.0'
|
||||
|
||||
testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test'
|
||||
}
|
172
checkstyle.xml
Normal file
172
checkstyle.xml
Normal file
@ -0,0 +1,172 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">
|
||||
|
||||
<!--
|
||||
|
||||
Checkstyle configuration that checks the sun coding conventions from:
|
||||
|
||||
- the Java Language Specification at
|
||||
http://java.sun.com/docs/books/jls/second_edition/html/index.html
|
||||
|
||||
- the Sun Code Conventions at http://java.sun.com/docs/codeconv/
|
||||
|
||||
- the Javadoc guidelines at
|
||||
http://java.sun.com/j2se/javadoc/writingdoccomments/index.html
|
||||
|
||||
- the JDK Api documentation http://java.sun.com/j2se/docs/api/index.html
|
||||
|
||||
- some best practices
|
||||
|
||||
Checkstyle is very configurable. Be sure to read the documentation at
|
||||
http://checkstyle.sf.net (or in your downloaded distribution).
|
||||
|
||||
Most Checks are configurable, be sure to consult the documentation.
|
||||
|
||||
To completely disable a check, just comment it out or delete it from the file.
|
||||
|
||||
Finally, it is worth reading the documentation.
|
||||
|
||||
-->
|
||||
|
||||
<module name="Checker">
|
||||
<!--
|
||||
If you set the basedir property below, then all reported file
|
||||
names will be relative to the specified directory. See
|
||||
http://checkstyle.sourceforge.net/5.x/config.html#Checker
|
||||
|
||||
<property name="basedir" value="${basedir}"/>
|
||||
-->
|
||||
|
||||
<property name="fileExtensions" value="java, properties, xml"/>
|
||||
|
||||
<!-- Checks that a package-info.java file exists for each package. -->
|
||||
<!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage -->
|
||||
<!--<module name="JavadocPackage"/>-->
|
||||
|
||||
<!-- Checks whether files end with a new line. -->
|
||||
<!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
|
||||
<!--<module name="NewlineAtEndOfFile"/>-->
|
||||
|
||||
<!-- Checks that property files contain the same keys. -->
|
||||
<!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
|
||||
<module name="Translation"/>
|
||||
|
||||
<!-- Checks for Size Violations. -->
|
||||
<!-- See http://checkstyle.sf.net/config_sizes.html -->
|
||||
<module name="FileLength"/>
|
||||
|
||||
<!-- Checks for whitespace -->
|
||||
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
|
||||
<module name="FileTabCharacter"/>
|
||||
|
||||
<!-- Miscellaneous other checks. -->
|
||||
<!-- See http://checkstyle.sf.net/config_misc.html -->
|
||||
<!--<module name="RegexpSingleline">
|
||||
<property name="format" value="\s+$"/>
|
||||
<property name="minimum" value="0"/>
|
||||
<property name="maximum" value="0"/>
|
||||
<property name="message" value="Line has trailing spaces."/>
|
||||
</module>-->
|
||||
|
||||
<!-- Checks for Headers -->
|
||||
<!-- See http://checkstyle.sf.net/config_header.html -->
|
||||
<!-- <module name="Header"> -->
|
||||
<!-- <property name="headerFile" value="${checkstyle.header.file}"/> -->
|
||||
<!-- <property name="fileExtensions" value="java"/> -->
|
||||
<!-- </module> -->
|
||||
|
||||
<module name="TreeWalker">
|
||||
|
||||
<!-- Checks for Javadoc comments. -->
|
||||
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
|
||||
<!--<module name="JavadocMethod"/>-->
|
||||
<!--<module name="JavadocType"/>-->
|
||||
<!--<module name="JavadocVariable"/>-->
|
||||
<!--<module name="JavadocStyle"/>-->
|
||||
|
||||
<!-- Checks for Naming Conventions. -->
|
||||
<!-- See http://checkstyle.sf.net/config_naming.html -->
|
||||
<module name="ConstantName"/>
|
||||
<module name="LocalFinalVariableName"/>
|
||||
<module name="LocalVariableName"/>
|
||||
<module name="MemberName"/>
|
||||
<module name="MethodName"/>
|
||||
<module name="PackageName"/>
|
||||
<module name="ParameterName"/>
|
||||
<module name="StaticVariableName"/>
|
||||
<module name="TypeName"/>
|
||||
|
||||
<!-- Checks for imports -->
|
||||
<!-- See http://checkstyle.sf.net/config_import.html -->
|
||||
<!--<module name="AvoidStarImport"/>-->
|
||||
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
|
||||
<module name="RedundantImport"/>
|
||||
<!--module name="UnusedImports">
|
||||
<property name="processJavadoc" value="false"/>
|
||||
</module-->
|
||||
|
||||
<!-- Checks for Size Violations. -->
|
||||
<!-- See http://checkstyle.sf.net/config_sizes.html -->
|
||||
<!--<module name="LineLength"/>-->
|
||||
<module name="MethodLength"/>
|
||||
<!--<module name="ParameterNumber"/>-->
|
||||
|
||||
<!-- Checks for whitespace -->
|
||||
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
|
||||
<module name="EmptyForIteratorPad"/>
|
||||
<!--<module name="GenericWhitespace"/>-->
|
||||
<!--<module name="MethodParamPad"/>-->
|
||||
<!--<module name="NoWhitespaceAfter"/>-->
|
||||
<module name="NoWhitespaceBefore"/>
|
||||
<!--<module name="OperatorWrap"/>-->
|
||||
<module name="ParenPad"/>
|
||||
<module name="TypecastParenPad"/>
|
||||
<!--<module name="WhitespaceAfter"/>-->
|
||||
<module name="WhitespaceAround"/>
|
||||
|
||||
<!-- Modifier Checks -->
|
||||
<!-- See http://checkstyle.sf.net/config_modifiers.html -->
|
||||
<!--<module name="ModifierOrder"/>-->
|
||||
<module name="RedundantModifier"/>
|
||||
|
||||
<!-- Checks for blocks. You know, those {}'s -->
|
||||
<!-- See http://checkstyle.sf.net/config_blocks.html -->
|
||||
<module name="AvoidNestedBlocks"/>
|
||||
<module name="EmptyBlock"/>
|
||||
<module name="LeftCurly"/>
|
||||
<!--<module name="NeedBraces"/>-->
|
||||
<module name="RightCurly"/>
|
||||
|
||||
<!-- Checks for common coding problems -->
|
||||
<!-- See http://checkstyle.sf.net/config_coding.html -->
|
||||
<!--<module name="AvoidInlineConditionals"/>-->
|
||||
<module name="EmptyStatement"/>
|
||||
<module name="EqualsHashCode"/>
|
||||
<!--<module name="HiddenField"/>-->
|
||||
<module name="IllegalInstantiation"/>
|
||||
<module name="InnerAssignment"/>
|
||||
<!--<module name="MagicNumber"/>-->
|
||||
<!--<module name="MissingSwitchDefault"/>-->
|
||||
<module name="SimplifyBooleanExpression"/>
|
||||
<module name="SimplifyBooleanReturn"/>
|
||||
|
||||
<!-- Checks for class design -->
|
||||
<!-- See http://checkstyle.sf.net/config_design.html -->
|
||||
<!--<module name="DesignForExtension"/>-->
|
||||
<!--<module name="FinalClass"/>-->
|
||||
<!--<module name="HideUtilityClassConstructor"/>-->
|
||||
<!--<module name="InterfaceIsType"/>-->
|
||||
<!--<module name="VisibilityModifier"/>-->
|
||||
|
||||
<!-- Miscellaneous other checks. -->
|
||||
<!-- See http://checkstyle.sf.net/config_misc.html -->
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<!--<module name="FinalParameters"/>-->
|
||||
<!--<module name="TodoComment"/>-->
|
||||
<!--<module name="UpperEll"/>-->
|
||||
|
||||
</module>
|
||||
|
||||
</module>
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
#Tue Feb 06 11:48:53 SAMT 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.5.1-bin.zip
|
169
gradlew
vendored
Executable file
169
gradlew
vendored
Executable file
@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
84
gradlew.bat
vendored
Normal file
84
gradlew.bat
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
112
papers.html
112
papers.html
@ -1,112 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>NG-Tacker</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Custom fonts for this template -->
|
||||
<link href="vendor/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="vendor/google/montserrat.css" rel="stylesheet" type="text/css">
|
||||
<link href='vendor/google/kaushan.css' rel='stylesheet' type='text/css'>
|
||||
<link href='vendor/google/droid.css' rel='stylesheet' type='text/css'>
|
||||
<link href='vendor/google/roboto.css' rel='stylesheet' type='text/css'>
|
||||
|
||||
<!-- Custom styles for this template -->
|
||||
<link href="css/agency.css" rel="stylesheet">
|
||||
|
||||
</head>
|
||||
|
||||
<body id="page-top">
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark fixed-top navbar-shrink" id="mainNav">
|
||||
<div class="container">
|
||||
<a class="navbar-brand js-scroll-trigger" href="#page-top">NG-Tracker</a>
|
||||
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse"
|
||||
data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false"
|
||||
aria-label="Toggle navigation">
|
||||
Menu
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||
<ul class="navbar-nav text-uppercase ml-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link js-scroll-trigger" target="_blank" href="#landing">НИО-17</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link js-scroll-trigger" target="_blank" href="http://is.ulstu.ru">Сайт кафедры</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link js-scroll-trigger" target="_blank" href="https://kias.rfbr.ru/">КИАС РФФИ</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link js-scroll-trigger" href="#logout">Выход</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section id="services">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
<h2 class="section-heading text-uppercase">Статьи</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row text-left">
|
||||
<div class="col-md-12">
|
||||
<span class="fa-stack fa-1x">
|
||||
<i class="fa fa-circle fa-stack-2x text-warning"></i>
|
||||
<i class="fa fa-file-text-o fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
<a href="paper.html?id=1"><span>А.А. Романов, И.А. Тимина. Обзор методов оценки трудоемкости производства</span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row text-left">
|
||||
<div class="col-md-12">
|
||||
<span class="fa-stack fa-1x">
|
||||
<i class="fa fa-circle fa-stack-2x text-primary"></i>
|
||||
<i class="fa fa-file-text-o fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
<a href="paper.html?id=2"><span>А.А. Филиппов, В.С.Мошкин, Н.Г. Ярушкина. Построение онтологии предметной ...</span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row text-left">
|
||||
<div class="col-md-12">
|
||||
<span class="fa-stack fa-1x">
|
||||
<i class="fa fa-circle fa-stack-2x text-success"></i>
|
||||
<i class="fa fa-file-text-o fa-stack-1x fa-inverse"></i>
|
||||
</span>
|
||||
<a href="paper.html?id=2"><span>А.А. Филиппов, В.С.Мошкин, Н.Г. Ярушкина. Построение онтологии предметной ...</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Bootstrap core JavaScript -->
|
||||
<script src="vendor/jquery/jquery.min.js"></script>
|
||||
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Plugin JavaScript -->
|
||||
<script src="vendor/jquery-easing/jquery.easing.min.js"></script>
|
||||
|
||||
<!-- Contact form JavaScript -->
|
||||
<script src="js/jqBootstrapValidation.js"></script>
|
||||
<script src="js/contact_me.js"></script>
|
||||
|
||||
<!-- Custom scripts for this template -->
|
||||
<script src="js/agency.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
14
src/main/java/ru/ulstu/NgTrackerApplication.java
Normal file
14
src/main/java/ru/ulstu/NgTrackerApplication.java
Normal file
@ -0,0 +1,14 @@
|
||||
package ru.ulstu;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import ru.ulstu.core.repository.JpaDetachableRepositoryImpl;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableJpaRepositories(repositoryBaseClass = JpaDetachableRepositoryImpl.class)
|
||||
public class NgTrackerApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(NgTrackerApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package ru.ulstu.commit.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.ulstu.configuration.Constants;
|
||||
import ru.ulstu.core.model.response.PageableItems;
|
||||
import ru.ulstu.commit.model.CommitListDto;
|
||||
import ru.ulstu.commit.service.CommitService;
|
||||
import ru.ulstu.core.model.response.Response;
|
||||
import ru.ulstu.odin.controller.OdinController;
|
||||
import ru.ulstu.odin.model.OdinVoid;
|
||||
|
||||
import static ru.ulstu.commit.controller.CommitController.URL;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(URL)
|
||||
public class CommitController extends OdinController<CommitListDto, OdinVoid> {
|
||||
public static final String URL = Constants.API_1_0 + "commits";
|
||||
private final CommitService commitService;
|
||||
|
||||
public CommitController(CommitService commitService) {
|
||||
super(CommitListDto.class);
|
||||
this.commitService = commitService;
|
||||
}
|
||||
|
||||
@GetMapping("")
|
||||
public Response<PageableItems<CommitListDto>> getCommits(@RequestParam(value = "offset", defaultValue = "0") int offset,
|
||||
@RequestParam(value = "count", defaultValue = "10") int count) {
|
||||
return new Response<>(commitService.getCommits(offset, count));
|
||||
}
|
||||
}
|
53
src/main/java/ru/ulstu/commit/model/CommitListDto.java
Normal file
53
src/main/java/ru/ulstu/commit/model/CommitListDto.java
Normal file
@ -0,0 +1,53 @@
|
||||
package ru.ulstu.commit.model;
|
||||
|
||||
import ru.ulstu.odin.model.annotation.OdinCaption;
|
||||
import ru.ulstu.odin.model.annotation.OdinDate;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CommitListDto {
|
||||
private final static String DELIMITER = ";";
|
||||
@OdinCaption("Дата")
|
||||
@OdinDate(type = OdinDate.OdinDateType.DATETIME)
|
||||
private final Date date;
|
||||
@OdinCaption("Пользователь")
|
||||
private final String userName;
|
||||
@OdinCaption("Сообщение")
|
||||
private final String message;
|
||||
|
||||
public CommitListDto(String data) {
|
||||
List<String> datas = Arrays.stream(data.split(DELIMITER))
|
||||
.filter(d -> d != null && !d.isEmpty())
|
||||
.collect(Collectors.toList());
|
||||
if (datas.size() > 2) {
|
||||
this.userName = datas.get(0);
|
||||
this.message = datas.get(2);
|
||||
SimpleDateFormat format = new SimpleDateFormat("EEE MMM dd hh:mm:ss yyyy Z", Locale.US);
|
||||
try {
|
||||
this.date = format.parse(datas.get(1));
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException("wrong commits date");
|
||||
}
|
||||
return;
|
||||
}
|
||||
throw new RuntimeException("wrong commits data");
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
}
|
62
src/main/java/ru/ulstu/commit/service/CommitService.java
Normal file
62
src/main/java/ru/ulstu/commit/service/CommitService.java
Normal file
@ -0,0 +1,62 @@
|
||||
package ru.ulstu.commit.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.ulstu.commit.model.CommitListDto;
|
||||
import ru.ulstu.core.model.response.PageableItems;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class CommitService {
|
||||
private static final String COMMITS_FILE_NAME = "/commits.log";
|
||||
|
||||
private final List<CommitListDto> commits;
|
||||
|
||||
public CommitService() {
|
||||
commits = getCommits();
|
||||
}
|
||||
|
||||
public PageableItems<CommitListDto> getCommits(int offset, int count) {
|
||||
return new PageableItems<>(commits.size(), commits.stream()
|
||||
.skip(offset)
|
||||
.limit(count)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private List<CommitListDto> getCommits() {
|
||||
return getFileContent()
|
||||
.stream()
|
||||
.map(CommitListDto::new)
|
||||
.filter(Objects::nonNull)
|
||||
.sorted((c1, c2) -> c2.getDate().compareTo(c1.getDate()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<String> getFileContent() {
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
//Get file from resources folder
|
||||
InputStream is = getClass().getResourceAsStream(COMMITS_FILE_NAME);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
||||
String line;
|
||||
try {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
result.add(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Collections.reverse(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
package ru.ulstu.configuration;
|
||||
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "ng-tracker")
|
||||
@Validated
|
||||
public class ApplicationProperties {
|
||||
@NotBlank
|
||||
private String baseUrl;
|
||||
@NotBlank
|
||||
private String undeadUserLogin;
|
||||
private boolean devMode;
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public String getUndeadUserLogin() {
|
||||
return undeadUserLogin;
|
||||
}
|
||||
|
||||
public void setUndeadUserLogin(String undeadUserLogin) {
|
||||
this.undeadUserLogin = undeadUserLogin;
|
||||
}
|
||||
|
||||
public boolean isDevMode() {
|
||||
return devMode;
|
||||
}
|
||||
|
||||
public void setDevMode(boolean devMode) {
|
||||
this.devMode = devMode;
|
||||
}
|
||||
}
|
39
src/main/java/ru/ulstu/configuration/AsyncConfiguration.java
Normal file
39
src/main/java/ru/ulstu/configuration/AsyncConfiguration.java
Normal file
@ -0,0 +1,39 @@
|
||||
package ru.ulstu.configuration;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
@EnableScheduling
|
||||
public class AsyncConfiguration implements AsyncConfigurer {
|
||||
private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
|
||||
|
||||
@Override
|
||||
@Bean(name = "taskExecutor")
|
||||
public Executor getAsyncExecutor() {
|
||||
log.debug("Creating Async Task Executor");
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(2);
|
||||
executor.setMaxPoolSize(2);
|
||||
executor.setQueueCapacity(500);
|
||||
executor.setThreadNamePrefix("balance-executor-");
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
|
||||
return new SimpleAsyncUncaughtExceptionHandler();
|
||||
}
|
||||
}
|
20
src/main/java/ru/ulstu/configuration/Constants.java
Normal file
20
src/main/java/ru/ulstu/configuration/Constants.java
Normal file
@ -0,0 +1,20 @@
|
||||
package ru.ulstu.configuration;
|
||||
|
||||
public class Constants {
|
||||
public static final String API_1_0 = "/api/1.0/";
|
||||
|
||||
public static final String MAIL_ACTIVATE = "Account activation";
|
||||
public static final String MAIL_RESET = "Password reset";
|
||||
public static final int MIN_PASSWORD_LENGTH = 6;
|
||||
|
||||
public static final String LOGIN_REGEX = "^[_'.@A-Za-z0-9-]*$";
|
||||
|
||||
public static final String COOKIES_NAME = "JSESSIONID";
|
||||
public static final String LOGOUT_URL = "/login?logout";
|
||||
public static final String SESSION_ID_ATTR = "sessionId";
|
||||
public static final int SESSION_TIMEOUT_SECONDS = 30 * 60;
|
||||
|
||||
public static final String PASSWORD_RESET_REQUEST_PAGE = "/resetRequest";
|
||||
public static final String PASSWORD_RESET_PAGE = "/reset";
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package ru.ulstu.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
@Configuration
|
||||
class ControllersConfiguration {
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
config.setAllowCredentials(true);
|
||||
config.addAllowedOrigin("*");
|
||||
config.addAllowedHeader("*");
|
||||
config.addAllowedMethod("GET");
|
||||
config.addAllowedMethod("POST");
|
||||
config.addAllowedMethod("DELETE");
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package ru.ulstu.configuration;
|
||||
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
|
||||
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.jetty.JettyServerCustomizer;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class HttpListenerConfiguration implements EmbeddedServletContainerCustomizer {
|
||||
@Value("${server.http.port}")
|
||||
private int httpPort;
|
||||
|
||||
private void configureJetty(JettyEmbeddedServletContainerFactory jettyFactory) {
|
||||
jettyFactory.addServerCustomizers((JettyServerCustomizer) server -> {
|
||||
ServerConnector serverConnector = new ServerConnector(server);
|
||||
serverConnector.setPort(httpPort);
|
||||
server.addConnector(serverConnector);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(ConfigurableEmbeddedServletContainer container) {
|
||||
if (container instanceof JettyEmbeddedServletContainerFactory) {
|
||||
configureJetty((JettyEmbeddedServletContainerFactory) container);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package ru.ulstu.configuration;
|
||||
|
||||
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
|
||||
import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class JacksonConfiguration {
|
||||
|
||||
@Bean
|
||||
public Hibernate5Module hibernate5Module() {
|
||||
return new Hibernate5Module();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AfterburnerModule afterburnerModule() {
|
||||
return new AfterburnerModule();
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package ru.ulstu.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Configuration
|
||||
public class MailTemplateConfiguration {
|
||||
@Bean
|
||||
public ClassLoaderTemplateResolver emailTemplateResolver() {
|
||||
ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
|
||||
emailTemplateResolver.setPrefix("mail_templates/");
|
||||
emailTemplateResolver.setSuffix(".html");
|
||||
emailTemplateResolver.setTemplateMode("HTML5");
|
||||
emailTemplateResolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
emailTemplateResolver.setOrder(1);
|
||||
return emailTemplateResolver;
|
||||
}
|
||||
}
|
24
src/main/java/ru/ulstu/configuration/MvcConfiguration.java
Normal file
24
src/main/java/ru/ulstu/configuration/MvcConfiguration.java
Normal file
@ -0,0 +1,24 @@
|
||||
package ru.ulstu.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
|
||||
@Configuration
|
||||
public class MvcConfiguration extends WebMvcConfigurerAdapter {
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("/{articlename:\\w+}");
|
||||
//registry.addViewController("/admin/{articlename:\\w+}");
|
||||
registry.addRedirectViewController("/", "/index");
|
||||
registry.addRedirectViewController("/default", "/index");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry
|
||||
.addResourceHandler("/webjars/**")
|
||||
.addResourceLocations("/webjars/");
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package ru.ulstu.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
@Configuration
|
||||
public class PasswordEncoderConfiguration {
|
||||
@Bean
|
||||
public BCryptPasswordEncoder bCryptPasswordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
118
src/main/java/ru/ulstu/configuration/SecurityConfiguration.java
Normal file
118
src/main/java/ru/ulstu/configuration/SecurityConfiguration.java
Normal file
@ -0,0 +1,118 @@
|
||||
package ru.ulstu.configuration;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
import ru.ulstu.user.controller.UserController;
|
||||
import ru.ulstu.user.model.UserRoleConstants;
|
||||
import ru.ulstu.user.service.UserService;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
|
||||
|
||||
@Value("${server.http.port}")
|
||||
private int httpPort;
|
||||
@Value("${server.port}")
|
||||
private int httpsPort;
|
||||
|
||||
private final UserService userService;
|
||||
private final BCryptPasswordEncoder bCryptPasswordEncoder;
|
||||
private final AuthenticationSuccessHandler authenticationSuccessHandler;
|
||||
private final LogoutSuccessHandler logoutSuccessHandler;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
public SecurityConfiguration(UserService userService,
|
||||
BCryptPasswordEncoder bCryptPasswordEncoder,
|
||||
AuthenticationSuccessHandler authenticationSuccessHandler,
|
||||
LogoutSuccessHandler logoutSuccessHandler,
|
||||
ApplicationProperties applicationProperties) {
|
||||
this.userService = userService;
|
||||
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
|
||||
this.authenticationSuccessHandler = authenticationSuccessHandler;
|
||||
this.logoutSuccessHandler = logoutSuccessHandler;
|
||||
this.applicationProperties = applicationProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf()
|
||||
.disable();
|
||||
if (applicationProperties.isDevMode()) {
|
||||
log.debug("Security disabled");
|
||||
http.authorizeRequests()
|
||||
.anyRequest()
|
||||
.permitAll();
|
||||
http.anonymous()
|
||||
.principal("developer")
|
||||
.authorities(UserRoleConstants.ADMIN);
|
||||
} else {
|
||||
log.debug("Security enabled");
|
||||
http.authorizeRequests()
|
||||
.antMatchers(UserController.ACTIVATE_URL).permitAll()
|
||||
.antMatchers(Constants.PASSWORD_RESET_REQUEST_PAGE).permitAll()
|
||||
.antMatchers(Constants.PASSWORD_RESET_PAGE).permitAll()
|
||||
.antMatchers(UserController.URL + UserController.REGISTER_URL).permitAll()
|
||||
.antMatchers(UserController.URL + UserController.ACTIVATE_URL).permitAll()
|
||||
.antMatchers(UserController.URL + UserController.PASSWORD_RESET_REQUEST_URL).permitAll()
|
||||
.antMatchers(UserController.URL + UserController.PASSWORD_RESET_URL).permitAll()
|
||||
.antMatchers("/swagger-ui.html").hasAuthority(UserRoleConstants.ADMIN)
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginPage("/login")
|
||||
.successHandler(authenticationSuccessHandler)
|
||||
.permitAll()
|
||||
.and()
|
||||
.logout()
|
||||
.logoutSuccessHandler(logoutSuccessHandler)
|
||||
.logoutSuccessUrl(Constants.LOGOUT_URL)
|
||||
.invalidateHttpSession(false)
|
||||
.clearAuthentication(true)
|
||||
.deleteCookies(Constants.COOKIES_NAME)
|
||||
.permitAll();
|
||||
}
|
||||
http.portMapper()
|
||||
.http(httpPort)
|
||||
.mapsTo(httpsPort)
|
||||
.and()
|
||||
.requiresChannel()
|
||||
.anyRequest()
|
||||
.requiresSecure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) {
|
||||
web.ignoring()
|
||||
.antMatchers("/css/**")
|
||||
.antMatchers("/js/**")
|
||||
.antMatchers("/templates/**")
|
||||
.antMatchers("/webjars/**");
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) {
|
||||
if (applicationProperties.isDevMode()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder);
|
||||
} catch (Exception e) {
|
||||
throw new BeanInitializationException("Security configuration failed", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package ru.ulstu.configuration;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
public class SwaggerConfiguration {
|
||||
@Bean
|
||||
public Docket swaggerApi() {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.any())
|
||||
.paths(Predicates.not(PathSelectors.regex("/error")))
|
||||
.build();
|
||||
}
|
||||
}
|
99
src/main/java/ru/ulstu/core/controller/AdviceController.java
Normal file
99
src/main/java/ru/ulstu/core/controller/AdviceController.java
Normal file
@ -0,0 +1,99 @@
|
||||
package ru.ulstu.core.controller;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.ulstu.core.error.EntityIdIsNullException;
|
||||
import ru.ulstu.core.model.ErrorConstants;
|
||||
import ru.ulstu.core.model.response.Response;
|
||||
import ru.ulstu.core.model.response.ResponseExtended;
|
||||
import ru.ulstu.user.error.*;
|
||||
import ru.ulstu.user.error.*;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@ControllerAdvice
|
||||
public class AdviceController {
|
||||
private final Logger log = LoggerFactory.getLogger(AdviceController.class);
|
||||
|
||||
private Response<Void> handleException(ErrorConstants error) {
|
||||
log.warn(error.toString());
|
||||
return new Response<>(error);
|
||||
}
|
||||
|
||||
private <E> ResponseExtended<E> handleException(ErrorConstants error, E errorData) {
|
||||
log.warn(error.toString());
|
||||
return new ResponseExtended<>(error, errorData);
|
||||
}
|
||||
|
||||
@ExceptionHandler(EntityIdIsNullException.class)
|
||||
public Response<Void> handleEntityIdIsNullException(Throwable e) {
|
||||
return handleException(ErrorConstants.ID_IS_NULL);
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseExtended<Set<String>> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
|
||||
final Set<String> errors = e.getBindingResult().getAllErrors().stream()
|
||||
.filter(error -> error instanceof FieldError)
|
||||
.map(error -> ((FieldError) error).getField())
|
||||
.collect(Collectors.toSet());
|
||||
return handleException(ErrorConstants.VALIDATION_ERROR, errors);
|
||||
}
|
||||
|
||||
@ExceptionHandler(UserIdExistsException.class)
|
||||
public Response<Void> handleUserIdExistsException(Throwable e) {
|
||||
return handleException(ErrorConstants.USER_ID_EXISTS);
|
||||
}
|
||||
|
||||
@ExceptionHandler(UserActivationError.class)
|
||||
public ResponseExtended<String> handleUserActivationError(Throwable e) {
|
||||
return handleException(ErrorConstants.USER_ACTIVATION_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(UserLoginExistsException.class)
|
||||
public ResponseExtended<String> handleUserLoginExistsException(Throwable e) {
|
||||
return handleException(ErrorConstants.USER_LOGIN_EXISTS, e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(UserEmailExistsException.class)
|
||||
public ResponseExtended<String> handleUserEmailExistsException(Throwable e) {
|
||||
return handleException(ErrorConstants.USER_EMAIL_EXISTS, e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(UserPasswordsNotValidOrNotMatchException.class)
|
||||
public Response<Void> handleUserPasswordsNotValidOrNotMatchException(Throwable e) {
|
||||
return handleException(ErrorConstants.USER_PASSWORDS_NOT_VALID_OR_NOT_MATCH);
|
||||
}
|
||||
|
||||
@ExceptionHandler(UserNotFoundException.class)
|
||||
public ResponseExtended<String> handleUserNotFoundException(Throwable e) {
|
||||
return handleException(ErrorConstants.USER_NOT_FOUND, e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(UserNotActivatedException.class)
|
||||
public Response<Void> handleUserNotActivatedException(Throwable e) {
|
||||
return handleException(ErrorConstants.USER_NOT_ACTIVATED);
|
||||
}
|
||||
|
||||
@ExceptionHandler(UserResetKeyError.class)
|
||||
public ResponseExtended<String> handleUserResetKeyError(Throwable e) {
|
||||
return handleException(ErrorConstants.USER_RESET_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(UserIsUndeadException.class)
|
||||
public ResponseExtended<String> handleUserIsUndeadException(Throwable e) {
|
||||
return handleException(ErrorConstants.USER_UNDEAD_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseExtended<String> handleUnknownException(Throwable e) {
|
||||
e.printStackTrace();
|
||||
return handleException(ErrorConstants.UNKNOWN, e.getMessage());
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package ru.ulstu.core.error;
|
||||
|
||||
public class EntityIdIsNullException extends RuntimeException {
|
||||
public EntityIdIsNullException() {
|
||||
}
|
||||
}
|
7
src/main/java/ru/ulstu/core/error/OdinException.java
Normal file
7
src/main/java/ru/ulstu/core/error/OdinException.java
Normal file
@ -0,0 +1,7 @@
|
||||
package ru.ulstu.core.error;
|
||||
|
||||
public class OdinException extends RuntimeException {
|
||||
public OdinException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
7
src/main/java/ru/ulstu/core/error/XlsLoadException.java
Normal file
7
src/main/java/ru/ulstu/core/error/XlsLoadException.java
Normal file
@ -0,0 +1,7 @@
|
||||
package ru.ulstu.core.error;
|
||||
|
||||
public class XlsLoadException extends Exception {
|
||||
public XlsLoadException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
7
src/main/java/ru/ulstu/core/error/XlsParseException.java
Normal file
7
src/main/java/ru/ulstu/core/error/XlsParseException.java
Normal file
@ -0,0 +1,7 @@
|
||||
package ru.ulstu.core.error;
|
||||
|
||||
public class XlsParseException extends Exception {
|
||||
public XlsParseException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
97
src/main/java/ru/ulstu/core/jpa/OffsetablePageRequest.java
Normal file
97
src/main/java/ru/ulstu/core/jpa/OffsetablePageRequest.java
Normal file
@ -0,0 +1,97 @@
|
||||
package ru.ulstu.core.jpa;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class OffsetablePageRequest implements Pageable, Serializable {
|
||||
private final int offset;
|
||||
private final int count;
|
||||
private final Sort sort;
|
||||
|
||||
public OffsetablePageRequest(int offset, int count) {
|
||||
this(offset, count, null);
|
||||
}
|
||||
|
||||
public OffsetablePageRequest(int offset, int count, Sort.Direction direction, String... properties) {
|
||||
this(offset, count, new Sort(direction, properties));
|
||||
}
|
||||
|
||||
public OffsetablePageRequest(int offset, int count, Sort sort) {
|
||||
if (offset < 0) {
|
||||
throw new IllegalArgumentException("Offset value must not be less than zero!");
|
||||
}
|
||||
if (count < 1) {
|
||||
throw new IllegalArgumentException("Count value must not be less than one!");
|
||||
}
|
||||
this.offset = offset;
|
||||
this.count = count;
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sort getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageSize() {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageNumber() {
|
||||
return offset / count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrevious() {
|
||||
return offset > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pageable next() {
|
||||
return new OffsetablePageRequest(getOffset() + getPageSize(), getPageSize(), getSort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pageable previousOrFirst() {
|
||||
return hasPrevious() ? previous() : first();
|
||||
}
|
||||
|
||||
public Pageable previous() {
|
||||
return getOffset() == 0 ? this : new OffsetablePageRequest(getOffset() - getPageSize(), getPageSize(), getSort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pageable first() {
|
||||
return new OffsetablePageRequest(0, getPageSize(), getSort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final OffsetablePageRequest other = (OffsetablePageRequest) obj;
|
||||
return this.offset == other.offset && this.count == other.count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + offset;
|
||||
result = prime * result + count;
|
||||
return result;
|
||||
}
|
||||
}
|
82
src/main/java/ru/ulstu/core/model/BaseEntity.java
Normal file
82
src/main/java/ru/ulstu/core/model/BaseEntity.java
Normal file
@ -0,0 +1,82 @@
|
||||
package ru.ulstu.core.model;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.io.Serializable;
|
||||
|
||||
@MappedSuperclass
|
||||
public abstract class BaseEntity implements Serializable, Comparable {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.TABLE)
|
||||
private Integer id;
|
||||
|
||||
@Version
|
||||
private Integer version;
|
||||
|
||||
public BaseEntity() {
|
||||
}
|
||||
|
||||
public BaseEntity(Integer id, Integer version) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!getClass().isAssignableFrom(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
BaseEntity other = (BaseEntity) obj;
|
||||
if (id == null) {
|
||||
if (other.id != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!id.equals(other.id)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (id == null ? 0 : id.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "{" +
|
||||
"id=" + id +
|
||||
", version=" + version +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Object o) {
|
||||
return id != null ? id.compareTo(((BaseEntity) o).getId()) : -1;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.id = null;
|
||||
this.version = null;
|
||||
}
|
||||
}
|
38
src/main/java/ru/ulstu/core/model/ErrorConstants.java
Normal file
38
src/main/java/ru/ulstu/core/model/ErrorConstants.java
Normal file
@ -0,0 +1,38 @@
|
||||
package ru.ulstu.core.model;
|
||||
|
||||
public enum ErrorConstants {
|
||||
UNKNOWN(0, "Unknown error"),
|
||||
ID_IS_NULL(1, "Id of entity has null value"),
|
||||
VALIDATION_ERROR(2, "Validation error"),
|
||||
USER_ID_EXISTS(100, "New user can't have id"),
|
||||
USER_ACTIVATION_ERROR(101, "Invalid activation key"),
|
||||
USER_EMAIL_EXISTS(102, "User with same email already exists"),
|
||||
USER_LOGIN_EXISTS(103, "User with same login already exists"),
|
||||
USER_PASSWORDS_NOT_VALID_OR_NOT_MATCH(104, "User passwords is not valid or not match"),
|
||||
USER_NOT_FOUND(105, "User is not found"),
|
||||
USER_NOT_ACTIVATED(106, "User is not activated"),
|
||||
USER_RESET_ERROR(107, "Invalid reset key"),
|
||||
USER_UNDEAD_ERROR(108, "Can't edit/delete that user"),
|
||||
FILE_UPLOAD_ERROR(110, "File upload error");
|
||||
|
||||
private int code;
|
||||
private String message;
|
||||
|
||||
ErrorConstants(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%d: %s", code, message);
|
||||
}
|
||||
}
|
34
src/main/java/ru/ulstu/core/model/TreeDto.java
Normal file
34
src/main/java/ru/ulstu/core/model/TreeDto.java
Normal file
@ -0,0 +1,34 @@
|
||||
package ru.ulstu.core.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TreeDto {
|
||||
private Integer id;
|
||||
private String text;
|
||||
private List<TreeDto> children = new ArrayList<>();
|
||||
|
||||
public TreeDto() {
|
||||
}
|
||||
|
||||
public <T extends TreeEntity> TreeDto(TreeEntity item) {
|
||||
this.text = item.toString();
|
||||
this.id = item.getId();
|
||||
}
|
||||
|
||||
public TreeDto(String rootName) {
|
||||
this.text = rootName;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public List<TreeDto> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
16
src/main/java/ru/ulstu/core/model/TreeEntity.java
Normal file
16
src/main/java/ru/ulstu/core/model/TreeEntity.java
Normal file
@ -0,0 +1,16 @@
|
||||
package ru.ulstu.core.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TreeEntity<T> {
|
||||
|
||||
Integer getId();
|
||||
|
||||
List<T> getChildren();
|
||||
|
||||
void setChildren(List<T> children);
|
||||
|
||||
T getParent();
|
||||
|
||||
void setParent(T parent);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package ru.ulstu.core.model.response;
|
||||
|
||||
class ControllerResponse<D, E> {
|
||||
private D data;
|
||||
private ControllerResponseError<E> error;
|
||||
|
||||
ControllerResponse(D data) {
|
||||
this.data = data;
|
||||
this.error = null;
|
||||
}
|
||||
|
||||
ControllerResponse(ControllerResponseError<E> error) {
|
||||
this.data = null;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public D getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public ControllerResponseError<E> getError() {
|
||||
return error;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package ru.ulstu.core.model.response;
|
||||
|
||||
import ru.ulstu.core.model.ErrorConstants;
|
||||
|
||||
class ControllerResponseError<D> {
|
||||
private ErrorConstants description;
|
||||
private D data;
|
||||
|
||||
ControllerResponseError(ErrorConstants description, D data) {
|
||||
this.description = description;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return description.getCode();
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return description.getMessage();
|
||||
}
|
||||
|
||||
public D getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package ru.ulstu.core.model.response;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class PageableItems<T> {
|
||||
private final long count;
|
||||
private final Collection<T> items;
|
||||
|
||||
public PageableItems(long count, Collection<T> items) {
|
||||
this.count = count;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public Collection<T> getItems() {
|
||||
return items;
|
||||
}
|
||||
}
|
16
src/main/java/ru/ulstu/core/model/response/Response.java
Normal file
16
src/main/java/ru/ulstu/core/model/response/Response.java
Normal file
@ -0,0 +1,16 @@
|
||||
package ru.ulstu.core.model.response;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import ru.ulstu.core.model.ErrorConstants;
|
||||
|
||||
public class Response<D> extends ResponseEntity<Object> {
|
||||
|
||||
public Response(D data) {
|
||||
super(new ControllerResponse<D, Void>(data), HttpStatus.OK);
|
||||
}
|
||||
|
||||
public Response(ErrorConstants error) {
|
||||
super(new ControllerResponse<Void, Void>(new ControllerResponseError<>(error, null)), HttpStatus.OK);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package ru.ulstu.core.model.response;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import ru.ulstu.core.model.ErrorConstants;
|
||||
|
||||
public class ResponseExtended<E> extends ResponseEntity<Object> {
|
||||
|
||||
public ResponseExtended(ErrorConstants error, E errorData) {
|
||||
super(new ControllerResponse<Void, E>(new ControllerResponseError<E>(error, errorData)), HttpStatus.OK);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package ru.ulstu.core.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@NoRepositoryBean
|
||||
public interface JpaDetachableRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
|
||||
void detach(T t);
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package ru.ulstu.core.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
|
||||
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class JpaDetachableRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
|
||||
implements JpaDetachableRepository<T, ID> {
|
||||
private EntityManager entityManager;
|
||||
|
||||
public JpaDetachableRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
|
||||
super(entityInformation, entityManager);
|
||||
this.entityManager = entityManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach(T t) {
|
||||
entityManager.detach(t);
|
||||
}
|
||||
}
|
31
src/main/java/ru/ulstu/core/service/TreeService.java
Normal file
31
src/main/java/ru/ulstu/core/service/TreeService.java
Normal file
@ -0,0 +1,31 @@
|
||||
package ru.ulstu.core.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.ulstu.core.model.TreeDto;
|
||||
import ru.ulstu.core.model.TreeEntity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Service
|
||||
public class TreeService<T extends TreeEntity> {
|
||||
public TreeDto getTree(String rootName, List<T> rootItems) {
|
||||
return addChildNode(new TreeDto(rootName), rootItems, element -> true);
|
||||
}
|
||||
|
||||
public TreeDto getTree(String rootName, List<T> rootItems, Predicate<T> filterPredicate) {
|
||||
return addChildNode(new TreeDto(rootName), rootItems, filterPredicate);
|
||||
}
|
||||
|
||||
private TreeDto addChildNode(TreeDto currentRoot, List<T> children, Predicate<T> filterPredicate) {
|
||||
if (children != null) {
|
||||
children.stream()
|
||||
.filter(filterPredicate)
|
||||
.forEach(item -> {
|
||||
TreeDto newNode = new TreeDto(item);
|
||||
currentRoot.getChildren().add(addChildNode(newNode, item.getChildren(), filterPredicate));
|
||||
});
|
||||
}
|
||||
return currentRoot;
|
||||
}
|
||||
}
|
207
src/main/java/ru/ulstu/core/service/XlsDocumentBuilder.java
Normal file
207
src/main/java/ru/ulstu/core/service/XlsDocumentBuilder.java
Normal file
@ -0,0 +1,207 @@
|
||||
package ru.ulstu.core.service;
|
||||
|
||||
import org.apache.poi.POIXMLDocument;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
import org.apache.poi.ss.util.RegionUtil;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import ru.ulstu.core.error.XlsParseException;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class XlsDocumentBuilder {
|
||||
private static final int DEFAULT_SHEET_NUM = 0;
|
||||
private File documentFile;
|
||||
private Workbook workBook;
|
||||
private Sheet currentSheet;
|
||||
|
||||
/**
|
||||
* Constructor for reading and writing data from/to *.[xls|xlsx] document
|
||||
*
|
||||
* @param file contains existing document for reading or new document to save
|
||||
*/
|
||||
public XlsDocumentBuilder(File file) throws IOException, XlsParseException {
|
||||
this.documentFile = file;
|
||||
if (file.exists()) {
|
||||
workBook = getWorkBook(file);
|
||||
currentSheet = workBook.getSheetAt(DEFAULT_SHEET_NUM);
|
||||
} else {
|
||||
workBook = new XSSFWorkbook();
|
||||
currentSheet = workBook.createSheet();
|
||||
}
|
||||
}
|
||||
|
||||
private Workbook getWorkBook(File file) throws XlsParseException, IOException {
|
||||
InputStream inputStream = new PushbackInputStream(new FileInputStream(file), 4096);
|
||||
if (isXlsx(inputStream)) {
|
||||
return new XSSFWorkbook(inputStream);
|
||||
} else if (isExcel(inputStream)) {
|
||||
return new HSSFWorkbook(inputStream);
|
||||
}
|
||||
throw new XlsParseException("Wrong document format");
|
||||
}
|
||||
|
||||
/**
|
||||
* Change active sheet to write or read data
|
||||
*
|
||||
* @param index index of sheet to activate
|
||||
*/
|
||||
public XlsDocumentBuilder setActiveSheet(int index) {
|
||||
workBook.setActiveSheet(index);
|
||||
currentSheet = workBook.getSheetAt(index);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new sheet in document and set it active
|
||||
*
|
||||
* @param sheetName
|
||||
*/
|
||||
public XlsDocumentBuilder insertNewSheet(String sheetName) {
|
||||
currentSheet = workBook.createSheet(sheetName);
|
||||
workBook.setActiveSheet(getSheetCount() - 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
public XlsDocumentBuilder insertNewSheet(String sheetName, int order) {
|
||||
insertNewSheet(sheetName);
|
||||
workBook.setSheetOrder(sheetName, order);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of sheet in document
|
||||
*
|
||||
* @return sheets count
|
||||
*/
|
||||
public int getSheetCount() {
|
||||
return workBook.getNumberOfSheets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of rows in sheet
|
||||
*
|
||||
* @return rows count
|
||||
*/
|
||||
public int getRowCount() {
|
||||
return currentSheet.getLastRowNum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of columns in sheet
|
||||
*
|
||||
* @return columns count
|
||||
*/
|
||||
public int getColumnCount() {
|
||||
Row row = currentSheet.getRow(getRowCount());
|
||||
if (row == null) {
|
||||
return 0;
|
||||
}
|
||||
return row.getLastCellNum() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns converted to string representation of cell value
|
||||
*
|
||||
* @param rowIndex row index of current sheet
|
||||
* @param colIndex column index of current sheet
|
||||
* @return string value of cell
|
||||
*/
|
||||
public String getCellAsString(int rowIndex, int colIndex) {
|
||||
Cell cell = currentSheet.getRow(rowIndex).getCell(colIndex);
|
||||
cell.setCellType(Cell.CELL_TYPE_STRING);
|
||||
return cell.getStringCellValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets string cell value
|
||||
*
|
||||
* @param rowIndex row index of current sheet
|
||||
* @param colIndex column index of current sheet
|
||||
*/
|
||||
public XlsDocumentBuilder setCellValue(int rowIndex, int colIndex, String value) {
|
||||
if (currentSheet.getRow(rowIndex) == null) {
|
||||
currentSheet.createRow(rowIndex);
|
||||
}
|
||||
if (currentSheet.getRow(rowIndex).getCell(colIndex) == null) {
|
||||
currentSheet.getRow(rowIndex).createCell(colIndex);
|
||||
}
|
||||
Cell cell = currentSheet.getRow(rowIndex).getCell(colIndex);
|
||||
cell.setCellValue(value);
|
||||
setColumnAutosize(colIndex, colIndex);
|
||||
return this;
|
||||
}
|
||||
|
||||
public XlsDocumentBuilder setCellValue(int rowIndex, int colIndex, int value) {
|
||||
return setCellValue(rowIndex, colIndex, String.valueOf(value));
|
||||
}
|
||||
|
||||
public XlsDocumentBuilder setCellValue(int rowIndex, int colIndex, long value) {
|
||||
return setCellValue(rowIndex, colIndex, String.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save current file
|
||||
*/
|
||||
public XlsDocumentBuilder save() throws IOException {
|
||||
OutputStream out = new FileOutputStream(documentFile);
|
||||
workBook.write(out);
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean isExcel(InputStream i) throws IOException {
|
||||
return (POIFSFileSystem.hasPOIFSHeader(i) || POIXMLDocument.hasOOXMLHeader(i));
|
||||
}
|
||||
|
||||
private boolean isXlsx(InputStream i) throws IOException {
|
||||
return POIXMLDocument.hasOOXMLHeader(i);
|
||||
}
|
||||
|
||||
public int getActiveSheetIndex() {
|
||||
return workBook.getActiveSheetIndex();
|
||||
}
|
||||
|
||||
public XlsDocumentBuilder mergeCells(int rowFrom, int rowTo, int colFrom, int colTo) {
|
||||
currentSheet.addMergedRegion(new CellRangeAddress(rowFrom, rowTo, colFrom, colTo));
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setRegionBorderWithMedium(int rowFrom, int rowTo, int colFrom, int colTo) {
|
||||
for (int row = rowFrom; row < rowTo; row++) {
|
||||
for (int col = colFrom; col <= colTo; col++) {
|
||||
CellRangeAddress cellRangeAddress = new CellRangeAddress(row, row, col, col);
|
||||
RegionUtil.setBorderBottom(CellStyle.BORDER_THIN, cellRangeAddress, currentSheet, workBook);
|
||||
RegionUtil.setBorderLeft(CellStyle.BORDER_THIN, cellRangeAddress, currentSheet, workBook);
|
||||
RegionUtil.setBorderRight(CellStyle.BORDER_THIN, cellRangeAddress, currentSheet, workBook);
|
||||
RegionUtil.setBorderTop(CellStyle.BORDER_THIN, cellRangeAddress, currentSheet, workBook);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public XlsDocumentBuilder setColumnAutosize(int from, int to) {
|
||||
for (int col = from; col <= to; col++) {
|
||||
currentSheet.autoSizeColumn(col, true);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public XlsDocumentBuilder setRowAutosize(int from, int to) {
|
||||
CellStyle style = workBook.createCellStyle();
|
||||
style.setWrapText(true);
|
||||
for (int row = from; row <= to; row++) {
|
||||
for (int col = 0; col <= currentSheet.getRow(row).getLastCellNum(); col++) {
|
||||
if (currentSheet.getRow(row).getCell(col) != null) {
|
||||
currentSheet.getRow(row).getCell(col).setCellStyle(style);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public XlsDocumentBuilder deleteSheet(int index) {
|
||||
workBook.removeSheetAt(index);
|
||||
return this;
|
||||
}
|
||||
}
|
46
src/main/java/ru/ulstu/core/util/DateUtils.java
Normal file
46
src/main/java/ru/ulstu/core/util/DateUtils.java
Normal file
@ -0,0 +1,46 @@
|
||||
package ru.ulstu.core.util;
|
||||
|
||||
import java.time.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class DateUtils {
|
||||
|
||||
public static Date clearTime(Date date) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(date);
|
||||
calendar.set(Calendar.HOUR, 0);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
public static Calendar getCalendar(Date date) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
return cal;
|
||||
}
|
||||
|
||||
public static List<Month> getMonths () {
|
||||
return Arrays.asList(Month.values());
|
||||
}
|
||||
|
||||
public static Date instantToDate(Instant instant) {
|
||||
return Date.from(instant);
|
||||
}
|
||||
|
||||
public static Date localDateToDate(LocalDate localDate) {
|
||||
return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
|
||||
public static Date localDateTimeToDate(LocalDateTime localDate) {
|
||||
return Date.from(localDate.atZone(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
|
||||
public static Date localTimeToDate(LocalTime localTime) {
|
||||
return Date.from(localTime.atDate(LocalDate.now()).atZone(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
}
|
17
src/main/java/ru/ulstu/core/util/NumberUtils.java
Normal file
17
src/main/java/ru/ulstu/core/util/NumberUtils.java
Normal file
@ -0,0 +1,17 @@
|
||||
package ru.ulstu.core.util;
|
||||
|
||||
public class NumberUtils {
|
||||
public static Double ceil(Double number) {
|
||||
if (number == null) {
|
||||
return 0.0;
|
||||
}
|
||||
return Double.valueOf(Math.ceil(number));
|
||||
}
|
||||
|
||||
public static Double round(Double number) {
|
||||
if (number == null) {
|
||||
return 0.0;
|
||||
}
|
||||
return Double.valueOf(Math.ceil(number * 100)) / 100;
|
||||
}
|
||||
}
|
12
src/main/java/ru/ulstu/core/util/StreamApiUtils.java
Normal file
12
src/main/java/ru/ulstu/core/util/StreamApiUtils.java
Normal file
@ -0,0 +1,12 @@
|
||||
package ru.ulstu.core.util;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StreamApiUtils {
|
||||
|
||||
public static <T, R> List<T> convert(List<R> entitites, Function<R, T> converter) {
|
||||
return entitites.stream().map(e -> converter.apply(e)).collect(Collectors.toList());
|
||||
}
|
||||
}
|
37
src/main/java/ru/ulstu/odin/controller/OdinController.java
Normal file
37
src/main/java/ru/ulstu/odin/controller/OdinController.java
Normal file
@ -0,0 +1,37 @@
|
||||
package ru.ulstu.odin.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import ru.ulstu.core.model.response.Response;
|
||||
import ru.ulstu.odin.model.OdinDto;
|
||||
import ru.ulstu.odin.model.OdinMetadata;
|
||||
import ru.ulstu.odin.service.OdinService;
|
||||
|
||||
public abstract class OdinController<L, E extends OdinDto> {
|
||||
public static final String META_LIST_URL = "/meta/list";
|
||||
public static final String META_ELEMENT_URL = "/meta/element";
|
||||
|
||||
private Class<L> listDtoClass;
|
||||
private Class<E> elementDtoClass;
|
||||
@Autowired
|
||||
private OdinService<L, E> odinService;
|
||||
|
||||
public OdinController(Class<L> listDtoClass) {
|
||||
this(listDtoClass, null);
|
||||
}
|
||||
|
||||
public OdinController(Class<L> listDtoClass, Class<E> elementDtoClass) {
|
||||
this.listDtoClass = listDtoClass;
|
||||
this.elementDtoClass = elementDtoClass;
|
||||
}
|
||||
|
||||
@GetMapping(META_LIST_URL)
|
||||
public Response<OdinMetadata> getListModel() {
|
||||
return new Response<>(odinService.getListModel(listDtoClass));
|
||||
}
|
||||
|
||||
@GetMapping(META_ELEMENT_URL)
|
||||
public Response<OdinMetadata> getElementModel() {
|
||||
return new Response<>(odinService.getElementModel(elementDtoClass));
|
||||
}
|
||||
}
|
9
src/main/java/ru/ulstu/odin/model/OdinBooleanField.java
Normal file
9
src/main/java/ru/ulstu/odin/model/OdinBooleanField.java
Normal file
@ -0,0 +1,9 @@
|
||||
package ru.ulstu.odin.model;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class OdinBooleanField extends OdinField {
|
||||
public OdinBooleanField(Field field) {
|
||||
super(field, OdinFieldType.BOOLEAN);
|
||||
}
|
||||
}
|
28
src/main/java/ru/ulstu/odin/model/OdinCollectionField.java
Normal file
28
src/main/java/ru/ulstu/odin/model/OdinCollectionField.java
Normal file
@ -0,0 +1,28 @@
|
||||
package ru.ulstu.odin.model;
|
||||
|
||||
import ru.ulstu.core.error.OdinException;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class OdinCollectionField extends OdinField {
|
||||
private final String path;
|
||||
|
||||
public OdinCollectionField(Field field) {
|
||||
super(field, OdinFieldType.COLLECTION);
|
||||
ParameterizedType genericType = (ParameterizedType) field.getGenericType();
|
||||
Type fieldElementClass = genericType.getActualTypeArguments()[0];
|
||||
try {
|
||||
OdinDto someInstance = (OdinDto) ((Class) (fieldElementClass)).newInstance();
|
||||
this.path = someInstance.getControllerPath();
|
||||
} catch (IllegalAccessException | InstantiationException e) {
|
||||
throw new OdinException(String.format("Can't create new instance, check default constructor of %s",
|
||||
fieldElementClass.getTypeName()));
|
||||
}
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
}
|
18
src/main/java/ru/ulstu/odin/model/OdinDateField.java
Normal file
18
src/main/java/ru/ulstu/odin/model/OdinDateField.java
Normal file
@ -0,0 +1,18 @@
|
||||
package ru.ulstu.odin.model;
|
||||
|
||||
import ru.ulstu.odin.model.annotation.OdinDate;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class OdinDateField extends OdinField {
|
||||
private final OdinDate.OdinDateType type;
|
||||
|
||||
public OdinDateField(Field field) {
|
||||
super(field, OdinFieldType.DATE);
|
||||
this.type = getValue(OdinDate.class, "type", OdinDate.OdinDateType.class);
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type.toString();
|
||||
}
|
||||
}
|
14
src/main/java/ru/ulstu/odin/model/OdinDto.java
Normal file
14
src/main/java/ru/ulstu/odin/model/OdinDto.java
Normal file
@ -0,0 +1,14 @@
|
||||
package ru.ulstu.odin.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public interface OdinDto {
|
||||
Object getId();
|
||||
|
||||
@JsonProperty("view")
|
||||
String getViewValue();
|
||||
|
||||
@JsonIgnore
|
||||
String getControllerPath();
|
||||
}
|
154
src/main/java/ru/ulstu/odin/model/OdinField.java
Normal file
154
src/main/java/ru/ulstu/odin/model/OdinField.java
Normal file
@ -0,0 +1,154 @@
|
||||
package ru.ulstu.odin.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
import ru.ulstu.core.error.OdinException;
|
||||
import ru.ulstu.odin.model.annotation.OdinCaption;
|
||||
import ru.ulstu.odin.model.annotation.OdinReadOnly;
|
||||
import ru.ulstu.odin.model.annotation.OdinVisible;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class OdinField implements Comparable {
|
||||
public enum OdinFieldType {
|
||||
BOOLEAN,
|
||||
DATE,
|
||||
NUMERIC,
|
||||
STRING,
|
||||
COLLECTION,
|
||||
OBJECT,
|
||||
UNKNOWN;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
private Field field;
|
||||
protected final OdinFieldType fieldType;
|
||||
protected final String fieldName;
|
||||
protected final String caption;
|
||||
protected final OdinVisible.OdinVisibleType visible;
|
||||
protected final boolean readOnly;
|
||||
protected final boolean notEmpty;
|
||||
|
||||
public OdinField(Field field, OdinFieldType fieldType) {
|
||||
this.field = field;
|
||||
this.fieldName = getFieldName(field);
|
||||
this.caption = Optional.ofNullable(getValueFromAnnotation(OdinCaption.class, "value"))
|
||||
.map(value -> cast(value, String.class))
|
||||
.orElse(fieldName);
|
||||
this.visible = getValue(OdinVisible.class, "type", OdinVisible.OdinVisibleType.class);
|
||||
this.readOnly = field.isAnnotationPresent(OdinReadOnly.class);
|
||||
this.notEmpty = field.isAnnotationPresent(NotBlank.class)
|
||||
|| field.isAnnotationPresent(NotEmpty.class)
|
||||
|| field.isAnnotationPresent(NotNull.class);
|
||||
this.fieldType = fieldType;
|
||||
}
|
||||
|
||||
private String getFieldName(Field field) {
|
||||
if (field.isAnnotationPresent(JsonProperty.class)) {
|
||||
final String fieldName = cast(getValueFromAnnotation(JsonProperty.class, "value"), String.class);
|
||||
return Optional.ofNullable(fieldName).filter(value -> !value.isEmpty()).orElse(field.getName());
|
||||
}
|
||||
return field.getName();
|
||||
}
|
||||
|
||||
private Object getDefaultValueFromAnnotation(Class<? extends Annotation> annotationClass, String valueName) {
|
||||
try {
|
||||
final Method method = annotationClass.getDeclaredMethod(valueName);
|
||||
return method.getDefaultValue();
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new OdinException(String.format("Default value %s is not found in annotation %s",
|
||||
valueName, annotationClass.getSimpleName()));
|
||||
}
|
||||
}
|
||||
|
||||
private Object getValueFromAnnotation(Class<? extends Annotation> annotationClass, String valueName) {
|
||||
if (!field.isAnnotationPresent(annotationClass)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final Method method = annotationClass.getDeclaredMethod(valueName);
|
||||
return method.invoke(field.getAnnotation(annotationClass));
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new OdinException(String.format("Value %s is not found in annotation %s",
|
||||
valueName, annotationClass.getSimpleName()));
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T cast(Object value, Class<T> clazz) {
|
||||
try {
|
||||
return clazz.cast(value);
|
||||
} catch (ClassCastException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> T getValue(Class<? extends Annotation> annotationClass, String valueName, Class<T> clazz) {
|
||||
if (field.isAnnotationPresent(annotationClass)) {
|
||||
return cast(getValueFromAnnotation(annotationClass, valueName), clazz);
|
||||
}
|
||||
return cast(getDefaultValueFromAnnotation(annotationClass, valueName), clazz);
|
||||
}
|
||||
|
||||
public String getFieldType() {
|
||||
return fieldType.toString();
|
||||
}
|
||||
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
public String getCaption() {
|
||||
return caption;
|
||||
}
|
||||
|
||||
public String getVisible() {
|
||||
return visible.toString();
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
public boolean isNotEmpty() {
|
||||
return notEmpty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
OdinField odinField = (OdinField) o;
|
||||
return Objects.equals(fieldName, odinField.fieldName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(fieldName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + " {" +
|
||||
"fieldName='" + fieldName + '\'' +
|
||||
", fieldType='" + fieldType + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Object o) {
|
||||
final String thisName = Optional.ofNullable(fieldName).orElse("");
|
||||
final String oName = Optional.ofNullable(o).map(obj -> ((OdinField) obj).fieldName).orElse("");
|
||||
return thisName.compareTo(oName);
|
||||
}
|
||||
}
|
26
src/main/java/ru/ulstu/odin/model/OdinMetadata.java
Normal file
26
src/main/java/ru/ulstu/odin/model/OdinMetadata.java
Normal file
@ -0,0 +1,26 @@
|
||||
package ru.ulstu.odin.model;
|
||||
|
||||
import ru.ulstu.core.error.OdinException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class OdinMetadata {
|
||||
private final boolean odinDto;
|
||||
private final List<OdinField> fields;
|
||||
|
||||
public OdinMetadata(boolean odinDto, List<OdinField> fields) {
|
||||
if (fields == null) {
|
||||
throw new OdinException("Fields list can't be null");
|
||||
}
|
||||
this.odinDto = odinDto;
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
public boolean isOdinDto() {
|
||||
return odinDto;
|
||||
}
|
||||
|
||||
public List<OdinField> getFields() {
|
||||
return fields;
|
||||
}
|
||||
}
|
30
src/main/java/ru/ulstu/odin/model/OdinNumericField.java
Normal file
30
src/main/java/ru/ulstu/odin/model/OdinNumericField.java
Normal file
@ -0,0 +1,30 @@
|
||||
package ru.ulstu.odin.model;
|
||||
|
||||
import ru.ulstu.odin.model.annotation.OdinNumeric;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class OdinNumericField extends OdinField {
|
||||
private final boolean positiveOnly;
|
||||
private final int precision;
|
||||
private final int scale;
|
||||
|
||||
public OdinNumericField(Field field) {
|
||||
super(field, OdinFieldType.NUMERIC);
|
||||
this.positiveOnly = getValue(OdinNumeric.class, "positiveOnly", Boolean.class);
|
||||
this.precision = getValue(OdinNumeric.class, "precision", Integer.class);
|
||||
this.scale = getValue(OdinNumeric.class, "scale", Integer.class);
|
||||
}
|
||||
|
||||
public boolean isPositiveOnly() {
|
||||
return positiveOnly;
|
||||
}
|
||||
|
||||
public int getPrecision() {
|
||||
return precision;
|
||||
}
|
||||
|
||||
public int getScale() {
|
||||
return scale;
|
||||
}
|
||||
}
|
26
src/main/java/ru/ulstu/odin/model/OdinObjectField.java
Normal file
26
src/main/java/ru/ulstu/odin/model/OdinObjectField.java
Normal file
@ -0,0 +1,26 @@
|
||||
package ru.ulstu.odin.model;
|
||||
|
||||
import ru.ulstu.core.error.OdinException;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class OdinObjectField extends OdinField {
|
||||
private final String path;
|
||||
|
||||
public OdinObjectField(Field field) {
|
||||
super(field, OdinFieldType.OBJECT);
|
||||
Type fieldElementClass = field.getType();
|
||||
try {
|
||||
OdinDto someInstance = (OdinDto) ((Class) (fieldElementClass)).newInstance();
|
||||
this.path = someInstance.getControllerPath();
|
||||
} catch (IllegalAccessException | InstantiationException e) {
|
||||
throw new OdinException(String.format("Can't create new instance, check default constructor of %s",
|
||||
fieldElementClass.getTypeName()));
|
||||
}
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
}
|
37
src/main/java/ru/ulstu/odin/model/OdinStringField.java
Normal file
37
src/main/java/ru/ulstu/odin/model/OdinStringField.java
Normal file
@ -0,0 +1,37 @@
|
||||
package ru.ulstu.odin.model;
|
||||
|
||||
import org.hibernate.validator.constraints.Email;
|
||||
import ru.ulstu.odin.model.annotation.OdinString;
|
||||
import ru.ulstu.odin.model.annotation.OdinString.OdinStringType;
|
||||
|
||||
import javax.validation.constraints.Size;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static ru.ulstu.odin.model.annotation.OdinString.OdinStringType.EMAIL;
|
||||
|
||||
public class OdinStringField extends OdinField {
|
||||
private final int minLength;
|
||||
private final int maxLength;
|
||||
private final OdinStringType type;
|
||||
|
||||
public OdinStringField(Field field) {
|
||||
super(field, OdinFieldType.STRING);
|
||||
this.minLength = getValue(Size.class, "min", Integer.class);
|
||||
this.maxLength = getValue(Size.class, "max", Integer.class);
|
||||
this.type = field.isAnnotationPresent(Email.class)
|
||||
? EMAIL
|
||||
: getValue(OdinString.class, "type", OdinStringType.class);
|
||||
}
|
||||
|
||||
public int getMinLength() {
|
||||
return minLength;
|
||||
}
|
||||
|
||||
public int getMaxLength() {
|
||||
return maxLength;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type.toString();
|
||||
}
|
||||
}
|
18
src/main/java/ru/ulstu/odin/model/OdinVoid.java
Normal file
18
src/main/java/ru/ulstu/odin/model/OdinVoid.java
Normal file
@ -0,0 +1,18 @@
|
||||
package ru.ulstu.odin.model;
|
||||
|
||||
public class OdinVoid implements OdinDto {
|
||||
@Override
|
||||
public Object getId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getViewValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControllerPath() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package ru.ulstu.odin.model.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {FIELD, ANNOTATION_TYPE})
|
||||
public @interface OdinCaption {
|
||||
String value();
|
||||
}
|
23
src/main/java/ru/ulstu/odin/model/annotation/OdinDate.java
Normal file
23
src/main/java/ru/ulstu/odin/model/annotation/OdinDate.java
Normal file
@ -0,0 +1,23 @@
|
||||
package ru.ulstu.odin.model.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {FIELD, ANNOTATION_TYPE})
|
||||
public @interface OdinDate {
|
||||
enum OdinDateType {
|
||||
DATETIME, DATE, TIME;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
OdinDateType type() default OdinDateType.DATE;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package ru.ulstu.odin.model.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {FIELD, ANNOTATION_TYPE})
|
||||
public @interface OdinNumeric {
|
||||
boolean positiveOnly() default false;
|
||||
|
||||
int precision() default 10;
|
||||
|
||||
int scale() default 0;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package ru.ulstu.odin.model.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {FIELD, ANNOTATION_TYPE})
|
||||
public @interface OdinReadOnly {
|
||||
}
|
23
src/main/java/ru/ulstu/odin/model/annotation/OdinString.java
Normal file
23
src/main/java/ru/ulstu/odin/model/annotation/OdinString.java
Normal file
@ -0,0 +1,23 @@
|
||||
package ru.ulstu.odin.model.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {FIELD, ANNOTATION_TYPE})
|
||||
public @interface OdinString {
|
||||
enum OdinStringType {
|
||||
STRING, PASSWORD, TEXT, EMAIL, HREF;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
OdinStringType type() default OdinStringType.STRING;
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package ru.ulstu.odin.model.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {FIELD, ANNOTATION_TYPE})
|
||||
public @interface OdinVisible {
|
||||
enum OdinVisibleType {
|
||||
ALL, ON_CREATE, ON_UPDATE, NONE;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
OdinVisibleType type() default OdinVisibleType.ALL;
|
||||
}
|
101
src/main/java/ru/ulstu/odin/service/OdinService.java
Normal file
101
src/main/java/ru/ulstu/odin/service/OdinService.java
Normal file
@ -0,0 +1,101 @@
|
||||
package ru.ulstu.odin.service;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.ulstu.core.error.OdinException;
|
||||
import ru.ulstu.odin.model.*;
|
||||
import ru.ulstu.odin.model.*;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class OdinService<L, E extends OdinDto> {
|
||||
private final Logger log = LoggerFactory.getLogger(OdinService.class);
|
||||
|
||||
private final Map<String, OdinField.OdinFieldType> aliases;
|
||||
|
||||
public OdinService() {
|
||||
aliases = new HashMap<>();
|
||||
aliases.put("boolean", OdinField.OdinFieldType.BOOLEAN);
|
||||
aliases.put("date", OdinField.OdinFieldType.DATE);
|
||||
aliases.put("instant", OdinField.OdinFieldType.DATE);
|
||||
aliases.put("localdate", OdinField.OdinFieldType.DATE);
|
||||
aliases.put("localtime", OdinField.OdinFieldType.DATE);
|
||||
aliases.put("localdatetime", OdinField.OdinFieldType.DATE);
|
||||
aliases.put("int", OdinField.OdinFieldType.NUMERIC);
|
||||
aliases.put("integer", OdinField.OdinFieldType.NUMERIC);
|
||||
aliases.put("double", OdinField.OdinFieldType.NUMERIC);
|
||||
aliases.put("float", OdinField.OdinFieldType.NUMERIC);
|
||||
aliases.put("long", OdinField.OdinFieldType.NUMERIC);
|
||||
aliases.put("string", OdinField.OdinFieldType.STRING);
|
||||
aliases.put("collection", OdinField.OdinFieldType.COLLECTION);
|
||||
aliases.put("object", OdinField.OdinFieldType.OBJECT);
|
||||
}
|
||||
|
||||
private OdinField.OdinFieldType getFieldTypeByTypeAlias(String fieldType) {
|
||||
return Optional.ofNullable(aliases.get(fieldType)).orElse(OdinField.OdinFieldType.UNKNOWN);
|
||||
}
|
||||
|
||||
private String getTypeAlias(String fieldType) {
|
||||
return fieldType.replaceAll(".*\\.", "").toLowerCase().trim();
|
||||
}
|
||||
|
||||
private OdinField.OdinFieldType getFieldType(Field field) {
|
||||
final String typeAlias;
|
||||
if (Collection.class.isAssignableFrom(field.getType())) {
|
||||
typeAlias = "collection";
|
||||
} else if (OdinDto.class.isAssignableFrom(field.getType())) {
|
||||
typeAlias = "object";
|
||||
} else {
|
||||
typeAlias = getTypeAlias(field.getGenericType().getTypeName());
|
||||
}
|
||||
return getFieldTypeByTypeAlias(typeAlias);
|
||||
}
|
||||
|
||||
private <T> List<OdinField> getDtoMetaModel(Class<T> dtoClass) {
|
||||
return Arrays.stream(dtoClass.getDeclaredFields())
|
||||
.filter(field -> !field.isAnnotationPresent(JsonIgnore.class)
|
||||
&& !Modifier.isStatic(field.getModifiers()))
|
||||
.map(field -> {
|
||||
OdinField.OdinFieldType fieldType = getFieldType(field);
|
||||
switch (fieldType) {
|
||||
case BOOLEAN:
|
||||
return new OdinBooleanField(field);
|
||||
case DATE:
|
||||
return new OdinDateField(field);
|
||||
case NUMERIC:
|
||||
return new OdinNumericField(field);
|
||||
case STRING:
|
||||
return new OdinStringField(field);
|
||||
case COLLECTION:
|
||||
return new OdinCollectionField(field);
|
||||
case OBJECT:
|
||||
return new OdinObjectField(field);
|
||||
default:
|
||||
log.debug("Unknown type {}. Skip field {} of DTO {}.",
|
||||
field.getGenericType().getTypeName(), field.getName(), dtoClass.getSimpleName());
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public OdinMetadata getListModel(Class<L> listDtoClass) {
|
||||
if (listDtoClass == null) {
|
||||
throw new OdinException("List DTO class is null");
|
||||
}
|
||||
return new OdinMetadata(OdinDto.class.isAssignableFrom(listDtoClass), getDtoMetaModel(listDtoClass));
|
||||
}
|
||||
|
||||
public OdinMetadata getElementModel(Class<E> elementDtoClass) {
|
||||
return elementDtoClass == null
|
||||
? null :
|
||||
new OdinMetadata(true, getDtoMetaModel(elementDtoClass));
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package ru.ulstu.odinexample.controller;
|
||||
|
||||
public class OdinExampleController {
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package ru.ulstu.odinexample.model;
|
||||
|
||||
public class OdinExampleDto {
|
||||
}
|
103
src/main/java/ru/ulstu/odinexample/model/OdinExampleListDto.java
Normal file
103
src/main/java/ru/ulstu/odinexample/model/OdinExampleListDto.java
Normal file
@ -0,0 +1,103 @@
|
||||
package ru.ulstu.odinexample.model;
|
||||
|
||||
import ru.ulstu.core.util.DateUtils;
|
||||
import ru.ulstu.odin.model.annotation.OdinCaption;
|
||||
import ru.ulstu.odin.model.annotation.OdinDate;
|
||||
import ru.ulstu.odin.model.annotation.OdinNumeric;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Date;
|
||||
|
||||
public class OdinExampleListDto {
|
||||
@OdinCaption("instant")
|
||||
@OdinDate(type = OdinDate.OdinDateType.DATETIME)
|
||||
private Instant instant;
|
||||
@OdinCaption("date")
|
||||
private Date date;
|
||||
@OdinCaption("localdate")
|
||||
private LocalDate localDate;
|
||||
@OdinCaption("localtime")
|
||||
@OdinDate(type = OdinDate.OdinDateType.TIME)
|
||||
private LocalTime localTime;
|
||||
@OdinCaption("localdatetime")
|
||||
@OdinDate(type = OdinDate.OdinDateType.DATETIME)
|
||||
private LocalDateTime localDateTime;
|
||||
@OdinCaption("int")
|
||||
private int intval;
|
||||
@OdinCaption("int+settings")
|
||||
@OdinNumeric(precision = 5, scale = 2)
|
||||
private int intvalset;
|
||||
@OdinCaption("float")
|
||||
private float floatval;
|
||||
@OdinCaption("double")
|
||||
private double aDouble;
|
||||
@OdinCaption("double+set")
|
||||
@OdinNumeric(precision = 5, scale = 3)
|
||||
private double aDoubles;
|
||||
@OdinCaption("int+positive")
|
||||
@OdinNumeric(positiveOnly = true, scale = 2)
|
||||
private int invalpos;
|
||||
|
||||
public OdinExampleListDto() {
|
||||
this.instant = Instant.now();
|
||||
this.date = new Date();
|
||||
this.localDate = LocalDate.now();
|
||||
this.localTime = LocalTime.now();
|
||||
this.localDateTime = LocalDateTime.now();
|
||||
intval = -134;
|
||||
intvalset = 1343423232;
|
||||
floatval = 2323.44F;
|
||||
aDouble = -232323.43434;
|
||||
aDoubles = 0.456456456;
|
||||
invalpos = -23232323;
|
||||
}
|
||||
|
||||
|
||||
public Date getInstant() {
|
||||
return DateUtils.instantToDate(instant);
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public Date getLocalDate() {
|
||||
return DateUtils.localDateToDate(localDate);
|
||||
}
|
||||
|
||||
public Date getLocalTime() {
|
||||
return DateUtils.localTimeToDate(localTime);
|
||||
}
|
||||
|
||||
public Date getLocalDateTime() {
|
||||
return DateUtils.localDateTimeToDate(localDateTime);
|
||||
}
|
||||
|
||||
public int getIntval() {
|
||||
return intval;
|
||||
}
|
||||
|
||||
public int getIntvalset() {
|
||||
return intvalset;
|
||||
}
|
||||
|
||||
public float getFloatval() {
|
||||
return floatval;
|
||||
}
|
||||
|
||||
public double getaDouble() {
|
||||
return aDouble;
|
||||
}
|
||||
|
||||
public double getaDoubles() {
|
||||
return aDoubles;
|
||||
}
|
||||
|
||||
public int getInvalpos() {
|
||||
return invalpos;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package ru.ulstu.odinexample.service;
|
||||
|
||||
public class OdinExampleService {
|
||||
}
|
23
src/main/java/ru/ulstu/user/component/IpAddressResolver.java
Normal file
23
src/main/java/ru/ulstu/user/component/IpAddressResolver.java
Normal file
@ -0,0 +1,23 @@
|
||||
package ru.ulstu.user.component;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
public final class IpAddressResolver {
|
||||
private static final String CLIENT_IP_HEADER = "Client-IP";
|
||||
private static final String FORWARDED_FOR_HEADER = "X-Forwarded-For";
|
||||
|
||||
public static String getRemoteAddr(HttpServletRequest request) {
|
||||
String headerClientIp = request.getHeader("");
|
||||
String headerXForwardedFor = request.getHeader(HttpServletRequest.FORM_AUTH);
|
||||
if (StringUtils.isEmpty(request.getRemoteAddr()) && !StringUtils.isEmpty(headerClientIp)) {
|
||||
return headerClientIp;
|
||||
}
|
||||
if (!StringUtils.isEmpty(headerXForwardedFor)) {
|
||||
return headerXForwardedFor;
|
||||
}
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package ru.ulstu.user.component;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.ulstu.configuration.Constants;
|
||||
import ru.ulstu.user.service.UserSessionService;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
public class UserSessionLoginHandler extends SavedRequestAwareAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
|
||||
private final Logger log = LoggerFactory.getLogger(UserSessionLoginHandler.class);
|
||||
private final UserSessionService userSessionService;
|
||||
|
||||
public UserSessionLoginHandler(UserSessionService userSessionService) {
|
||||
super();
|
||||
this.userSessionService = userSessionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Authentication authentication) throws IOException, ServletException {
|
||||
super.onAuthenticationSuccess(request, response, authentication);
|
||||
final String login = authentication.getName();
|
||||
final String ipAddress = IpAddressResolver.getRemoteAddr(request);
|
||||
final String host = request.getRemoteHost();
|
||||
log.debug("Authentication Success for {}@{} ({})", login, ipAddress, host);
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
final String sessionId = session.getId();
|
||||
userSessionService.createUserSession(sessionId, login, ipAddress, host);
|
||||
session.setAttribute(Constants.SESSION_ID_ATTR, sessionId);
|
||||
session.setMaxInactiveInterval(Constants.SESSION_TIMEOUT_SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package ru.ulstu.user.component;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.ulstu.configuration.Constants;
|
||||
import ru.ulstu.user.service.UserSessionService;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
public class UserSessionLogoutHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
|
||||
private final Logger log = LoggerFactory.getLogger(UserSessionLogoutHandler.class);
|
||||
private final UserSessionService userSessionService;
|
||||
|
||||
public UserSessionLogoutHandler(UserSessionService userSessionService) {
|
||||
this.userSessionService = userSessionService;
|
||||
setDefaultTargetUrl(Constants.LOGOUT_URL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLogoutSuccess(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Authentication authentication) throws IOException, ServletException {
|
||||
if (authentication == null) {
|
||||
super.onLogoutSuccess(request, response, authentication);
|
||||
return;
|
||||
}
|
||||
final String login = authentication.getName();
|
||||
final String ipAddress = IpAddressResolver.getRemoteAddr(request);
|
||||
final String host = request.getRemoteHost();
|
||||
log.debug("Logout Success for {}@{} ({})", login, ipAddress, host);
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
final String sessionId = session.getAttribute(Constants.SESSION_ID_ATTR).toString();
|
||||
userSessionService.closeUserSession(sessionId);
|
||||
session.removeAttribute(Constants.SESSION_ID_ATTR);
|
||||
session.invalidate();
|
||||
}
|
||||
super.onLogoutSuccess(request, response, authentication);
|
||||
}
|
||||
}
|
158
src/main/java/ru/ulstu/user/controller/UserController.java
Normal file
158
src/main/java/ru/ulstu/user/controller/UserController.java
Normal file
@ -0,0 +1,158 @@
|
||||
package ru.ulstu.user.controller;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.ulstu.configuration.Constants;
|
||||
import ru.ulstu.core.model.response.PageableItems;
|
||||
import ru.ulstu.core.model.response.Response;
|
||||
import ru.ulstu.odin.controller.OdinController;
|
||||
import ru.ulstu.odin.model.OdinMetadata;
|
||||
import ru.ulstu.odin.model.OdinVoid;
|
||||
import ru.ulstu.odin.service.OdinService;
|
||||
import ru.ulstu.user.model.*;
|
||||
import ru.ulstu.user.service.UserService;
|
||||
import ru.ulstu.user.service.UserSessionService;
|
||||
import ru.ulstu.user.model.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import static ru.ulstu.user.controller.UserController.URL;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(URL)
|
||||
public class UserController extends OdinController<UserListDto, UserDto> {
|
||||
public static final String URL = Constants.API_1_0 + "users";
|
||||
public static final String ROLES_URL = "/roles";
|
||||
public static final String ROLES_META_URL = ROLES_URL + OdinController.META_LIST_URL;
|
||||
public static final String SESSIONS_URL = "/sessions";
|
||||
public static final String SESSIONS_META_URL = SESSIONS_URL + OdinController.META_LIST_URL;
|
||||
public static final String REGISTER_URL = "/register";
|
||||
public static final String ACTIVATE_URL = "/activate";
|
||||
public static final String PASSWORD_RESET_REQUEST_URL = "/password-reset-request";
|
||||
public static final String PASSWORD_RESET_URL = "/password-reset";
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(UserController.class);
|
||||
|
||||
private final UserService userService;
|
||||
private final UserSessionService userSessionService;
|
||||
private final OdinService<UserRoleDto, UserRoleDto> odinRolesService;
|
||||
private final OdinService<UserSessionListDto, OdinVoid> odinSessionsService;
|
||||
|
||||
public UserController(UserService userService,
|
||||
UserSessionService userSessionService,
|
||||
OdinService<UserRoleDto, UserRoleDto> odinRolesService,
|
||||
OdinService<UserSessionListDto, OdinVoid> odinSessionsService) {
|
||||
super(UserListDto.class, UserDto.class);
|
||||
this.userService = userService;
|
||||
this.userSessionService = userSessionService;
|
||||
this.odinRolesService = odinRolesService;
|
||||
this.odinSessionsService = odinSessionsService;
|
||||
}
|
||||
|
||||
@GetMapping(ROLES_URL)
|
||||
@Secured(UserRoleConstants.ADMIN)
|
||||
public Response<PageableItems<UserRoleDto>> getUserRoles() {
|
||||
log.debug("REST: UserController.getUserRoles()");
|
||||
return new Response<>(userService.getUserRoles());
|
||||
}
|
||||
|
||||
@GetMapping(ROLES_META_URL)
|
||||
@Secured(UserRoleConstants.ADMIN)
|
||||
public Response<OdinMetadata> getUserRolesMetaData() {
|
||||
log.debug("REST: UserController.getUserRolesMetaData()");
|
||||
return new Response<>(odinRolesService.getListModel(UserRoleDto.class));
|
||||
}
|
||||
|
||||
@GetMapping(SESSIONS_URL)
|
||||
@Secured(UserRoleConstants.ADMIN)
|
||||
public Response<PageableItems<UserSessionListDto>> getUserSessions(@RequestParam(value = "offset", defaultValue = "0") int offset,
|
||||
@RequestParam(value = "count", defaultValue = "10") int count) {
|
||||
log.debug("REST: UserController.getUserSessions()");
|
||||
return new Response<>(userSessionService.getSessions(offset, count));
|
||||
}
|
||||
|
||||
@GetMapping(SESSIONS_META_URL)
|
||||
@Secured(UserRoleConstants.ADMIN)
|
||||
public Response<OdinMetadata> getUserSessionsMetaData() {
|
||||
log.debug("REST: UserController.getUserSessionsMetaData()");
|
||||
return new Response<>(odinSessionsService.getListModel(UserSessionListDto.class));
|
||||
}
|
||||
|
||||
@GetMapping("")
|
||||
@Secured(UserRoleConstants.ADMIN)
|
||||
public Response<PageableItems<UserListDto>> getAllUsers(@RequestParam(value = "offset", defaultValue = "0") int offset,
|
||||
@RequestParam(value = "count", defaultValue = "10") int count) {
|
||||
log.debug("REST: UserController.getAllUsers( {}, {} )", offset, count);
|
||||
return new Response<>(userService.getAllUsers(offset, count));
|
||||
}
|
||||
|
||||
@GetMapping("/{userId}")
|
||||
@Secured(UserRoleConstants.ADMIN)
|
||||
public Response<UserDto> getUser(@PathVariable Integer userId) {
|
||||
log.debug("REST: UserController.getUser( {} )", userId);
|
||||
return new Response<>(userService.getUserWithRolesById(userId));
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("")
|
||||
@Secured(UserRoleConstants.ADMIN)
|
||||
public Response<UserDto> createUser(@Valid @RequestBody UserDto userDto) {
|
||||
log.debug("REST: UserController.createUser( {} )", userDto.getLogin());
|
||||
return new Response<>(userService.createUser(userDto));
|
||||
}
|
||||
|
||||
@PutMapping("")
|
||||
@Secured(UserRoleConstants.ADMIN)
|
||||
public Response<UserDto> updateUser(@Valid @RequestBody UserDto userDto) {
|
||||
log.debug("REST: UserController.updateUser( {} )", userDto.getLogin());
|
||||
return new Response<>(userService.updateUser(userDto));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{userId}")
|
||||
@Secured(UserRoleConstants.ADMIN)
|
||||
public Response<UserDto> deleteUser(@PathVariable Integer userId) {
|
||||
log.debug("REST: UserController.deleteUser( {} )", userId);
|
||||
return new Response<>(userService.deleteUser(userId));
|
||||
}
|
||||
|
||||
@PostMapping(REGISTER_URL)
|
||||
public Response<UserDto> registerUser(@Valid @RequestBody UserDto userDto) {
|
||||
log.debug("REST: UserController.registerUser( {} )", userDto.getLogin());
|
||||
return new Response<>(userService.createUser(userDto));
|
||||
}
|
||||
|
||||
@PostMapping(ACTIVATE_URL)
|
||||
public Response<UserDto> activateUser(@RequestParam("key") String activationKey) {
|
||||
log.debug("REST: UserController.activateUser( {} )", activationKey);
|
||||
return new Response<>(userService.activateUser(activationKey));
|
||||
}
|
||||
|
||||
// TODO: add page for user edit (user-profile)
|
||||
@PostMapping("/change-information")
|
||||
public Response<UserDto> changeInformation(@Valid @RequestBody UserDto userDto) {
|
||||
log.debug("REST: UserController.changeInformation( {} )", userDto.getLogin());
|
||||
return new Response<>(userService.updateUserInformation(userDto));
|
||||
}
|
||||
|
||||
// TODO: add page for user password change (user-profile)
|
||||
@PostMapping("/change-password")
|
||||
public Response<UserDto> changePassword(@Valid @RequestBody UserDto userDto) {
|
||||
log.debug("REST: UserController.changePassword( {} )", userDto.getLogin());
|
||||
return new Response<>(userService.changeUserPassword(userDto));
|
||||
}
|
||||
|
||||
@PostMapping(PASSWORD_RESET_REQUEST_URL)
|
||||
public Response<Boolean> requestPasswordReset(@RequestParam("email") String email) {
|
||||
log.debug("REST: UserController.requestPasswordReset( {} )", email);
|
||||
return new Response<>(userService.requestUserPasswordReset(email));
|
||||
}
|
||||
|
||||
@PostMapping(PASSWORD_RESET_URL)
|
||||
public Response<Boolean> finishPasswordReset(@RequestParam("key") String key,
|
||||
@RequestBody UserResetPasswordDto userResetPasswordDto) {
|
||||
log.debug("REST: UserController.requestPasswordReset( {} )", key);
|
||||
return new Response<>(userService.completeUserPasswordReset(key, userResetPasswordDto));
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package ru.ulstu.user.error;
|
||||
|
||||
public class UserActivationError extends RuntimeException {
|
||||
public UserActivationError(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package ru.ulstu.user.error;
|
||||
|
||||
public class UserEmailExistsException extends RuntimeException {
|
||||
public UserEmailExistsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package ru.ulstu.user.error;
|
||||
|
||||
public class UserIdExistsException extends RuntimeException {
|
||||
public UserIdExistsException() {
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package ru.ulstu.user.error;
|
||||
|
||||
public class UserIsUndeadException extends RuntimeException {
|
||||
public UserIsUndeadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package ru.ulstu.user.error;
|
||||
|
||||
public class UserLoginExistsException extends RuntimeException {
|
||||
public UserLoginExistsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package ru.ulstu.user.error;
|
||||
|
||||
public class UserNotActivatedException extends RuntimeException {
|
||||
public UserNotActivatedException() {
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package ru.ulstu.user.error;
|
||||
|
||||
public class UserNotFoundException extends RuntimeException {
|
||||
public UserNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package ru.ulstu.user.error;
|
||||
|
||||
public class UserPasswordsNotValidOrNotMatchException extends RuntimeException {
|
||||
public UserPasswordsNotValidOrNotMatchException() {
|
||||
}
|
||||
}
|
7
src/main/java/ru/ulstu/user/error/UserResetKeyError.java
Normal file
7
src/main/java/ru/ulstu/user/error/UserResetKeyError.java
Normal file
@ -0,0 +1,7 @@
|
||||
package ru.ulstu.user.error;
|
||||
|
||||
public class UserResetKeyError extends RuntimeException {
|
||||
public UserResetKeyError(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
170
src/main/java/ru/ulstu/user/model/User.java
Normal file
170
src/main/java/ru/ulstu/user/model/User.java
Normal file
@ -0,0 +1,170 @@
|
||||
package ru.ulstu.user.model;
|
||||
|
||||
import org.hibernate.annotations.BatchSize;
|
||||
import org.hibernate.validator.constraints.Email;
|
||||
import ru.ulstu.configuration.Constants;
|
||||
import ru.ulstu.core.model.BaseEntity;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
public class User extends BaseEntity {
|
||||
@NotNull
|
||||
@Pattern(regexp = Constants.LOGIN_REGEX)
|
||||
@Size(min = 1, max = 50)
|
||||
@Column(length = 50, unique = true, nullable = false)
|
||||
private String login;
|
||||
|
||||
@NotNull
|
||||
@Size(min = 60, max = 60)
|
||||
@Column(name = "password_hash", length = 60, nullable = false)
|
||||
private String password;
|
||||
|
||||
@NotNull
|
||||
@Size(max = 50)
|
||||
@Column(name = "first_name", length = 50, nullable = false)
|
||||
private String firstName;
|
||||
|
||||
@NotNull
|
||||
@Size(max = 50)
|
||||
@Column(name = "last_name", length = 50, nullable = false)
|
||||
private String lastName;
|
||||
|
||||
@NotNull
|
||||
@Email
|
||||
@Size(min = 5, max = 100)
|
||||
@Column(length = 100, nullable = false, unique = true)
|
||||
private String email;
|
||||
|
||||
@NotNull
|
||||
@Column(nullable = false)
|
||||
private boolean activated;
|
||||
|
||||
@Size(max = 20)
|
||||
@Column(name = "activation_key", length = 20)
|
||||
private String activationKey;
|
||||
|
||||
@Column(name = "activation_date")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date activationDate;
|
||||
|
||||
@Size(max = 20)
|
||||
@Column(name = "reset_key", length = 20)
|
||||
private String resetKey;
|
||||
|
||||
@Column(name = "reset_date")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date resetDate;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(
|
||||
name = "user_role",
|
||||
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
|
||||
inverseJoinColumns = {@JoinColumn(name = "user_role_name", referencedColumnName = "name")})
|
||||
@BatchSize(size = 20)
|
||||
private Set<UserRole> roles;
|
||||
|
||||
public User() {
|
||||
roles = new HashSet<>();
|
||||
activated = false;
|
||||
activationDate = new Date();
|
||||
resetDate = null;
|
||||
}
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login.toLowerCase();
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public boolean getActivated() {
|
||||
return activated;
|
||||
}
|
||||
|
||||
public void setActivated(boolean activated) {
|
||||
this.activated = activated;
|
||||
}
|
||||
|
||||
public String getActivationKey() {
|
||||
return activationKey;
|
||||
}
|
||||
|
||||
public void setActivationKey(String activationKey) {
|
||||
this.activationKey = activationKey;
|
||||
}
|
||||
|
||||
public Date getActivationDate() {
|
||||
return activationDate;
|
||||
}
|
||||
|
||||
public void setActivationDate(Date activationDate) {
|
||||
this.activationDate = activationDate;
|
||||
}
|
||||
|
||||
public String getResetKey() {
|
||||
return resetKey;
|
||||
}
|
||||
|
||||
public void setResetKey(String resetKey) {
|
||||
this.resetKey = resetKey;
|
||||
}
|
||||
|
||||
public Date getResetDate() {
|
||||
return resetDate;
|
||||
}
|
||||
|
||||
public void setResetDate(Date resetDate) {
|
||||
this.resetDate = resetDate;
|
||||
}
|
||||
|
||||
public Set<UserRole> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(Collection<UserRole> roles) {
|
||||
this.roles.clear();
|
||||
this.roles.addAll(roles);
|
||||
}
|
||||
}
|
193
src/main/java/ru/ulstu/user/model/UserDto.java
Normal file
193
src/main/java/ru/ulstu/user/model/UserDto.java
Normal file
@ -0,0 +1,193 @@
|
||||
package ru.ulstu.user.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.hibernate.validator.constraints.Email;
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
import org.springframework.util.StringUtils;
|
||||
import ru.ulstu.configuration.Constants;
|
||||
import ru.ulstu.odin.model.OdinDto;
|
||||
import ru.ulstu.odin.model.annotation.OdinCaption;
|
||||
import ru.ulstu.odin.model.annotation.OdinReadOnly;
|
||||
import ru.ulstu.odin.model.annotation.OdinString;
|
||||
import ru.ulstu.odin.model.annotation.OdinVisible;
|
||||
import ru.ulstu.user.controller.UserController;
|
||||
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ru.ulstu.odin.model.annotation.OdinString.OdinStringType.PASSWORD;
|
||||
|
||||
public class UserDto implements OdinDto {
|
||||
@OdinReadOnly
|
||||
private Integer id;
|
||||
|
||||
@NotBlank
|
||||
@Pattern(regexp = Constants.LOGIN_REGEX)
|
||||
@Size(min = 4, max = 50)
|
||||
@OdinCaption("Логин")
|
||||
private String login;
|
||||
|
||||
@NotBlank
|
||||
@Size(min = 2, max = 50)
|
||||
@OdinCaption("Имя")
|
||||
private String firstName;
|
||||
|
||||
@NotBlank
|
||||
@Size(min = 2, max = 50)
|
||||
@OdinCaption("Фамилия")
|
||||
private String lastName;
|
||||
|
||||
@Email
|
||||
@NotBlank
|
||||
@Size(min = 5, max = 100)
|
||||
@OdinCaption("E-Mail")
|
||||
private String email;
|
||||
|
||||
@OdinCaption("Аккаунт активен")
|
||||
private boolean activated;
|
||||
|
||||
@OdinCaption("Роли")
|
||||
private LinkedHashSet<UserRoleDto> roles;
|
||||
|
||||
@OdinString(type = PASSWORD)
|
||||
@OdinVisible(type = OdinVisible.OdinVisibleType.ON_UPDATE)
|
||||
@OdinCaption("Текущий пароль")
|
||||
@Size(max = 50)
|
||||
private String oldPassword;
|
||||
|
||||
@OdinString(type = PASSWORD)
|
||||
@OdinCaption("Пароль")
|
||||
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
||||
private String password;
|
||||
|
||||
@OdinString(type = PASSWORD)
|
||||
@OdinCaption("Пароль (подтверждение)")
|
||||
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
||||
private String passwordConfirm;
|
||||
|
||||
public UserDto() {
|
||||
activated = false;
|
||||
roles = new LinkedHashSet<>();
|
||||
}
|
||||
|
||||
public UserDto(User user) {
|
||||
this();
|
||||
this.id = user.getId();
|
||||
this.login = user.getLogin();
|
||||
this.firstName = user.getFirstName();
|
||||
this.lastName = user.getLastName();
|
||||
this.email = user.getEmail();
|
||||
this.activated = user.getActivated();
|
||||
this.roles.addAll(user.getRoles().stream()
|
||||
.map(UserRoleDto::new)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getViewValue() {
|
||||
return login;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControllerPath() {
|
||||
return UserController.URL;
|
||||
}
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public boolean isActivated() {
|
||||
return activated;
|
||||
}
|
||||
|
||||
public void setActivated(boolean activated) {
|
||||
this.activated = activated;
|
||||
}
|
||||
|
||||
public Set<UserRoleDto> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(Collection<UserRoleDto> roles) {
|
||||
this.roles.clear();
|
||||
this.roles.addAll(roles);
|
||||
}
|
||||
|
||||
public String getOldPassword() {
|
||||
return oldPassword;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getPasswordConfirm() {
|
||||
return passwordConfirm;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isPasswordsValid() {
|
||||
if (StringUtils.isEmpty(password) || StringUtils.isEmpty(passwordConfirm)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(password, passwordConfirm);
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isOldPasswordValid() {
|
||||
return !StringUtils.isEmpty(oldPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + " {" +
|
||||
"id=" + id +
|
||||
", login='" + login + '\'' +
|
||||
", firstName='" + firstName + '\'' +
|
||||
", lastName='" + lastName + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", activated=" + activated +
|
||||
", roles=" + roles +
|
||||
", password='" + password + '\'' +
|
||||
", passwordConfirm='" + passwordConfirm + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
76
src/main/java/ru/ulstu/user/model/UserListDto.java
Normal file
76
src/main/java/ru/ulstu/user/model/UserListDto.java
Normal file
@ -0,0 +1,76 @@
|
||||
package ru.ulstu.user.model;
|
||||
|
||||
import ru.ulstu.odin.model.OdinDto;
|
||||
import ru.ulstu.odin.model.annotation.OdinCaption;
|
||||
import ru.ulstu.odin.model.annotation.OdinVisible;
|
||||
import ru.ulstu.user.controller.UserController;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public class UserListDto implements OdinDto {
|
||||
@OdinVisible(type = OdinVisible.OdinVisibleType.NONE)
|
||||
private int id;
|
||||
@OdinCaption("Логин")
|
||||
private String login;
|
||||
@OdinCaption("Имя")
|
||||
private String firstName;
|
||||
@OdinCaption("Фамилия")
|
||||
private String lastName;
|
||||
@OdinCaption("E-Mail")
|
||||
private String email;
|
||||
@OdinCaption("Аккаунт активен")
|
||||
private boolean activated;
|
||||
@OdinCaption("Права администратора")
|
||||
private boolean admin;
|
||||
|
||||
public UserListDto(User user) {
|
||||
this.id = user.getId();
|
||||
this.login = user.getLogin();
|
||||
this.firstName = user.getFirstName();
|
||||
this.lastName = user.getLastName();
|
||||
this.email = user.getEmail();
|
||||
this.activated = user.getActivated();
|
||||
this.admin = Optional.ofNullable(user.getRoles()).orElse(Collections.emptySet()).stream()
|
||||
.anyMatch(role -> Objects.equals(UserRoleConstants.ADMIN, role.getName()));
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getViewValue() {
|
||||
return login;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControllerPath() {
|
||||
return UserController.URL;
|
||||
}
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public boolean isActivated() {
|
||||
return activated;
|
||||
}
|
||||
|
||||
public boolean isAdmin() {
|
||||
return admin;
|
||||
}
|
||||
}
|
28
src/main/java/ru/ulstu/user/model/UserResetPasswordDto.java
Normal file
28
src/main/java/ru/ulstu/user/model/UserResetPasswordDto.java
Normal file
@ -0,0 +1,28 @@
|
||||
package ru.ulstu.user.model;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
import ru.ulstu.configuration.Constants;
|
||||
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.Objects;
|
||||
|
||||
public class UserResetPasswordDto {
|
||||
@NotEmpty
|
||||
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
||||
private String password;
|
||||
@NotEmpty
|
||||
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
||||
private String passwordConfirm;
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getPasswordConfirm() {
|
||||
return passwordConfirm;
|
||||
}
|
||||
|
||||
public boolean isPasswordsValid() {
|
||||
return Objects.equals(password, passwordConfirm);
|
||||
}
|
||||
}
|
46
src/main/java/ru/ulstu/user/model/UserRole.java
Normal file
46
src/main/java/ru/ulstu/user/model/UserRole.java
Normal file
@ -0,0 +1,46 @@
|
||||
package ru.ulstu.user.model;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Entity
|
||||
@Table(name = "user_roles")
|
||||
public class UserRole {
|
||||
@Id
|
||||
@NotNull
|
||||
@Size(max = 50)
|
||||
@Column(length = 50, nullable = false)
|
||||
private String name;
|
||||
|
||||
public UserRole() {
|
||||
}
|
||||
|
||||
public UserRole(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
UserRole role = (UserRole) o;
|
||||
return !(name != null ? !name.equals(role.name) : role.name != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name != null ? name.hashCode() : 0;
|
||||
}
|
||||
}
|
6
src/main/java/ru/ulstu/user/model/UserRoleConstants.java
Normal file
6
src/main/java/ru/ulstu/user/model/UserRoleConstants.java
Normal file
@ -0,0 +1,6 @@
|
||||
package ru.ulstu.user.model;
|
||||
|
||||
public class UserRoleConstants {
|
||||
public static final String ADMIN = "ROLE_ADMIN";
|
||||
public static final String USER = "ROLE_USER";
|
||||
}
|
36
src/main/java/ru/ulstu/user/model/UserRoleDto.java
Normal file
36
src/main/java/ru/ulstu/user/model/UserRoleDto.java
Normal file
@ -0,0 +1,36 @@
|
||||
package ru.ulstu.user.model;
|
||||
|
||||
import ru.ulstu.odin.model.OdinDto;
|
||||
import ru.ulstu.odin.model.annotation.OdinCaption;
|
||||
import ru.ulstu.user.controller.UserController;
|
||||
|
||||
public class UserRoleDto implements OdinDto {
|
||||
@OdinCaption("Роль")
|
||||
private String id;
|
||||
|
||||
public UserRoleDto() {
|
||||
}
|
||||
|
||||
public UserRoleDto(UserRole role) {
|
||||
this.id = role.getName();
|
||||
}
|
||||
|
||||
public UserRoleDto(String name) {
|
||||
this.id = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getViewValue() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControllerPath() {
|
||||
return UserController.URL + UserController.ROLES_URL;
|
||||
}
|
||||
}
|
75
src/main/java/ru/ulstu/user/model/UserSession.java
Normal file
75
src/main/java/ru/ulstu/user/model/UserSession.java
Normal file
@ -0,0 +1,75 @@
|
||||
package ru.ulstu.user.model;
|
||||
|
||||
import ru.ulstu.core.model.BaseEntity;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Date;
|
||||
|
||||
@Entity
|
||||
@Table(name = "user_sessions")
|
||||
public class UserSession extends BaseEntity {
|
||||
@NotNull
|
||||
@Column(name = "session_id", nullable = false, unique = true)
|
||||
private String sessionId;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "ip_address", nullable = false)
|
||||
private String ipAddress;
|
||||
|
||||
@NotNull
|
||||
@Column(nullable = false)
|
||||
private String host;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "login_time", nullable = false)
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date loginTime;
|
||||
|
||||
@Column(name = "logout_time")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date logoutTime;
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
|
||||
public UserSession() {
|
||||
}
|
||||
|
||||
public UserSession(String sessionId, String ipAddress, String host, User user) {
|
||||
this.sessionId = sessionId;
|
||||
this.ipAddress = ipAddress;
|
||||
this.host = host;
|
||||
this.loginTime = new Date();
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public Date getLoginTime() {
|
||||
return loginTime;
|
||||
}
|
||||
|
||||
public Date getLogoutTime() {
|
||||
return logoutTime;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
this.logoutTime = new Date();
|
||||
}
|
||||
}
|
56
src/main/java/ru/ulstu/user/model/UserSessionListDto.java
Normal file
56
src/main/java/ru/ulstu/user/model/UserSessionListDto.java
Normal file
@ -0,0 +1,56 @@
|
||||
package ru.ulstu.user.model;
|
||||
|
||||
import ru.ulstu.odin.model.annotation.OdinCaption;
|
||||
import ru.ulstu.odin.model.annotation.OdinDate;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class UserSessionListDto {
|
||||
@OdinCaption("Сессия")
|
||||
private String sessionId;
|
||||
@OdinCaption("Пользователь")
|
||||
private String login;
|
||||
@OdinCaption("IP адрес")
|
||||
private String ipAddress;
|
||||
@OdinCaption("Хост")
|
||||
private String host;
|
||||
@OdinCaption("Вход")
|
||||
@OdinDate(type = OdinDate.OdinDateType.DATETIME)
|
||||
private Date loginTime;
|
||||
@OdinCaption("Выход")
|
||||
@OdinDate(type = OdinDate.OdinDateType.DATETIME)
|
||||
private Date logoutTime;
|
||||
|
||||
public UserSessionListDto(UserSession userSession) {
|
||||
this.sessionId = userSession.getSessionId();
|
||||
this.login = userSession.getUser().getLogin();
|
||||
this.ipAddress = userSession.getIpAddress();
|
||||
this.host = userSession.getHost();
|
||||
this.loginTime = userSession.getLoginTime();
|
||||
this.logoutTime = userSession.getLogoutTime();
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public Date getLoginTime() {
|
||||
return loginTime;
|
||||
}
|
||||
|
||||
public Date getLogoutTime() {
|
||||
return logoutTime;
|
||||
}
|
||||
}
|
28
src/main/java/ru/ulstu/user/repository/UserRepository.java
Normal file
28
src/main/java/ru/ulstu/user/repository/UserRepository.java
Normal file
@ -0,0 +1,28 @@
|
||||
package ru.ulstu.user.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import ru.ulstu.user.model.User;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public interface UserRepository extends JpaRepository<User, Integer> {
|
||||
User findOneByActivationKey(String activationKey);
|
||||
|
||||
List<User> findAllByActivatedIsFalseAndActivationDateBefore(Date date);
|
||||
|
||||
User findOneByResetKey(String resetKey);
|
||||
|
||||
List<User> findAllByResetKeyNotNullAndResetDateBefore(Date date);
|
||||
|
||||
User findOneByEmailIgnoreCase(String email);
|
||||
|
||||
User findOneByLoginIgnoreCase(String login);
|
||||
|
||||
@EntityGraph(attributePaths = "roles")
|
||||
User findOneWithRolesById(int id);
|
||||
|
||||
@EntityGraph(attributePaths = "roles")
|
||||
User findOneWithRolesByLogin(String login);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package ru.ulstu.user.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import ru.ulstu.user.model.UserRole;
|
||||
|
||||
public interface UserRoleRepository extends JpaRepository<UserRole, String> {
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package ru.ulstu.user.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import ru.ulstu.user.model.UserSession;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public interface UserSessionRepository extends JpaRepository<UserSession, Integer> {
|
||||
UserSession findOneBySessionId(String sessionId);
|
||||
|
||||
List<UserSession> findAllByLogoutTimeIsNullAndLoginTimeBefore(Date date);
|
||||
}
|
50
src/main/java/ru/ulstu/user/scheduler/UserScheduler.java
Normal file
50
src/main/java/ru/ulstu/user/scheduler/UserScheduler.java
Normal file
@ -0,0 +1,50 @@
|
||||
package ru.ulstu.user.scheduler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.ulstu.core.util.DateUtils;
|
||||
import ru.ulstu.user.model.User;
|
||||
import ru.ulstu.user.repository.UserRepository;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class UserScheduler {
|
||||
private final Logger log = LoggerFactory.getLogger(UserScheduler.class);
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public UserScheduler(UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0 1 * * ?")
|
||||
public void removeNotActivatedUsers() {
|
||||
log.debug("UserScheduler.removeNotActivatedUsers started");
|
||||
List<User> users = userRepository.findAllByActivatedIsFalseAndActivationDateBefore(
|
||||
DateUtils.instantToDate(Instant.now().minus(3, ChronoUnit.DAYS)));
|
||||
users.forEach(user -> {
|
||||
log.debug("Deleting not activated user {}", user.getLogin());
|
||||
userRepository.delete(user);
|
||||
});
|
||||
log.debug("UserScheduler.removeNotActivatedUsers finished");
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0 1 * * ?")
|
||||
public void removeUnusedRestKeyRequestsOfUsers() {
|
||||
log.debug("UserScheduler.removeUnusedRestKeyRequestsOfUsers started");
|
||||
List<User> users = userRepository.findAllByResetKeyNotNullAndResetDateBefore(
|
||||
DateUtils.instantToDate(Instant.now().minus(7, ChronoUnit.DAYS)));
|
||||
users.forEach(user -> {
|
||||
log.debug("Deleting old reset key request of user {}", user.getLogin());
|
||||
user.setResetKey(null);
|
||||
user.setResetDate(null);
|
||||
userRepository.save(user);
|
||||
});
|
||||
log.debug("UserScheduler.removeUnusedRestKeyRequestsOfUsers finished");
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package ru.ulstu.user.scheduler;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.ulstu.core.util.DateUtils;
|
||||
import ru.ulstu.user.model.UserSession;
|
||||
import ru.ulstu.user.repository.UserSessionRepository;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class UserSessionScheduler {
|
||||
private final Logger log = LoggerFactory.getLogger(UserSessionScheduler.class);
|
||||
|
||||
private final UserSessionRepository userSessionRepository;
|
||||
|
||||
public UserSessionScheduler(UserSessionRepository userSessionRepository) {
|
||||
this.userSessionRepository = userSessionRepository;
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0 1 * * ?")
|
||||
public void closeOldSessions() {
|
||||
log.debug("UserSessionScheduler.closeOldSessions started");
|
||||
final List<UserSession> sessions = userSessionRepository.findAllByLogoutTimeIsNullAndLoginTimeBefore(
|
||||
DateUtils.instantToDate(Instant.now().minus(1, ChronoUnit.DAYS)));
|
||||
sessions.forEach(session -> {
|
||||
log.debug("Close session {}", session.getSessionId());
|
||||
session.close();
|
||||
userSessionRepository.save(session);
|
||||
});
|
||||
log.debug("UserSessionScheduler.closeOldSessions finished");
|
||||
}
|
||||
}
|
78
src/main/java/ru/ulstu/user/service/MailService.java
Normal file
78
src/main/java/ru/ulstu/user/service/MailService.java
Normal file
@ -0,0 +1,78 @@
|
||||
package ru.ulstu.user.service;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.autoconfigure.mail.MailProperties;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thymeleaf.context.Context;
|
||||
import org.thymeleaf.spring4.SpringTemplateEngine;
|
||||
import ru.ulstu.configuration.ApplicationProperties;
|
||||
import ru.ulstu.configuration.Constants;
|
||||
import ru.ulstu.user.model.User;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Service
|
||||
public class MailService {
|
||||
private final Logger log = LoggerFactory.getLogger(MailService.class);
|
||||
|
||||
private static final String USER = "user";
|
||||
private static final String BASE_URL = "baseUrl";
|
||||
|
||||
private final JavaMailSender javaMailSender;
|
||||
private final SpringTemplateEngine templateEngine;
|
||||
private final MailProperties mailProperties;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
public MailService(JavaMailSender javaMailSender, SpringTemplateEngine templateEngine,
|
||||
MailProperties mailProperties, ApplicationProperties applicationProperties) {
|
||||
this.javaMailSender = javaMailSender;
|
||||
this.templateEngine = templateEngine;
|
||||
this.mailProperties = mailProperties;
|
||||
this.applicationProperties = applicationProperties;
|
||||
}
|
||||
|
||||
@Async
|
||||
public void sendEmail(String to, String subject, String content) {
|
||||
log.debug("Send email to '{}' with subject '{}'", to, subject);
|
||||
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
|
||||
try {
|
||||
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, false, StandardCharsets.UTF_8.name());
|
||||
message.setTo(to);
|
||||
message.setFrom(mailProperties.getUsername());
|
||||
message.setSubject(subject);
|
||||
message.setText(content, true);
|
||||
javaMailSender.send(mimeMessage);
|
||||
log.debug("Sent email to User '{}'", to);
|
||||
} catch (Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.warn("Email could not be sent to user '{}'", to, e);
|
||||
} else {
|
||||
log.warn("Email could not be sent to user '{}': {}", to, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Async
|
||||
public void sendEmailFromTemplate(User user, String templateName, String subject) {
|
||||
Context context = new Context();
|
||||
context.setVariable(USER, user);
|
||||
context.setVariable(BASE_URL, applicationProperties.getBaseUrl());
|
||||
String content = templateEngine.process(templateName, context);
|
||||
sendEmail(user.getEmail(), subject, content);
|
||||
}
|
||||
|
||||
@Async
|
||||
public void sendActivationEmail(User user) {
|
||||
sendEmailFromTemplate(user, "activationEmail", Constants.MAIL_ACTIVATE);
|
||||
}
|
||||
|
||||
@Async
|
||||
public void sendPasswordResetMail(User user) {
|
||||
sendEmailFromTemplate(user, "passwordResetEmail", Constants.MAIL_RESET);
|
||||
}
|
||||
}
|
66
src/main/java/ru/ulstu/user/service/UserMapper.java
Normal file
66
src/main/java/ru/ulstu/user/service/UserMapper.java
Normal file
@ -0,0 +1,66 @@
|
||||
package ru.ulstu.user.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.ulstu.user.model.*;
|
||||
import ru.ulstu.user.model.*;
|
||||
import ru.ulstu.user.repository.UserRoleRepository;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class UserMapper {
|
||||
private final UserRoleRepository userRoleRepository;
|
||||
|
||||
public UserMapper(UserRoleRepository userRoleRepository) {
|
||||
this.userRoleRepository = userRoleRepository;
|
||||
}
|
||||
|
||||
public Set<UserRole> rolesFromDto(Set<UserRoleDto> strings) {
|
||||
return Optional.ofNullable(strings).orElse(Collections.emptySet()).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(role -> userRoleRepository.findOne(role.getId().toString()))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public UserDto userEntityToUserDto(User userEntity) {
|
||||
if (userEntity == null) {
|
||||
return null;
|
||||
}
|
||||
return new UserDto(userEntity);
|
||||
}
|
||||
|
||||
public UserListDto userEntityToUserListDto(User userEntity) {
|
||||
if (userEntity == null) {
|
||||
return null;
|
||||
}
|
||||
return new UserListDto(userEntity);
|
||||
}
|
||||
|
||||
public List<UserListDto> userEntitiesToUserListDtos(List<User> userEntities) {
|
||||
return userEntities.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(this::userEntityToUserListDto)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public User userDtoToUserEntity(UserDto userDto) {
|
||||
if (userDto == null) {
|
||||
return null;
|
||||
}
|
||||
final User user = new User();
|
||||
user.setId(userDto.getId());
|
||||
user.setLogin(userDto.getLogin());
|
||||
user.setFirstName(userDto.getFirstName());
|
||||
user.setLastName(userDto.getLastName());
|
||||
user.setEmail(userDto.getEmail());
|
||||
user.setActivated(userDto.isActivated());
|
||||
final Set<UserRole> roles = this.rolesFromDto(userDto.getRoles());
|
||||
if (!roles.isEmpty()) {
|
||||
user.setRoles(roles);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
}
|
285
src/main/java/ru/ulstu/user/service/UserService.java
Normal file
285
src/main/java/ru/ulstu/user/service/UserService.java
Normal file
@ -0,0 +1,285 @@
|
||||
package ru.ulstu.user.service;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import ru.ulstu.configuration.ApplicationProperties;
|
||||
import ru.ulstu.core.error.EntityIdIsNullException;
|
||||
import ru.ulstu.core.jpa.OffsetablePageRequest;
|
||||
import ru.ulstu.core.model.BaseEntity;
|
||||
import ru.ulstu.core.model.response.PageableItems;
|
||||
import ru.ulstu.user.error.*;
|
||||
import ru.ulstu.user.model.*;
|
||||
import ru.ulstu.user.repository.UserRepository;
|
||||
import ru.ulstu.user.repository.UserRoleRepository;
|
||||
import ru.ulstu.user.util.UserUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class UserService implements UserDetailsService {
|
||||
private final Logger log = LoggerFactory.getLogger(UserService.class);
|
||||
private final UserRepository userRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final UserRoleRepository userRoleRepository;
|
||||
private final UserMapper userMapper;
|
||||
private final MailService mailService;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
public UserService(UserRepository userRepository,
|
||||
PasswordEncoder passwordEncoder,
|
||||
UserRoleRepository userRoleRepository,
|
||||
UserMapper userMapper,
|
||||
MailService mailService,
|
||||
ApplicationProperties applicationProperties) {
|
||||
this.userRepository = userRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.userRoleRepository = userRoleRepository;
|
||||
this.userMapper = userMapper;
|
||||
this.mailService = mailService;
|
||||
this.applicationProperties = applicationProperties;
|
||||
}
|
||||
|
||||
private User getUserByEmail(String email) {
|
||||
return userRepository.findOneByEmailIgnoreCase(email);
|
||||
}
|
||||
|
||||
private User getUserByActivationKey(String activationKey) {
|
||||
return userRepository.findOneByActivationKey(activationKey);
|
||||
}
|
||||
|
||||
public User getUserByLogin(String login) {
|
||||
return userRepository.findOneByLoginIgnoreCase(login);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public UserDto getUserWithRolesById(Integer userId) {
|
||||
final User userEntity = userRepository.findOneWithRolesById(userId);
|
||||
if (userEntity == null) {
|
||||
throw new UserNotFoundException(userId.toString());
|
||||
}
|
||||
return userMapper.userEntityToUserDto(userEntity);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public PageableItems<UserListDto> getAllUsers(int offset, int count) {
|
||||
final Page<User> page = userRepository.findAll(new OffsetablePageRequest(offset, count, new Sort("id")));
|
||||
return new PageableItems<>(page.getTotalElements(), userMapper.userEntitiesToUserListDtos(page.getContent()));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public PageableItems<UserRoleDto> getUserRoles() {
|
||||
final List<UserRoleDto> roles = userRoleRepository.findAll().stream()
|
||||
.map(UserRoleDto::new)
|
||||
.sorted(Comparator.comparing(UserRoleDto::getViewValue))
|
||||
.collect(Collectors.toList());
|
||||
return new PageableItems<>(roles.size(), roles);
|
||||
}
|
||||
|
||||
public UserDto createUser(UserDto userDto) {
|
||||
if (userDto.getId() != null) {
|
||||
throw new UserIdExistsException();
|
||||
}
|
||||
if (getUserByLogin(userDto.getLogin()) != null) {
|
||||
throw new UserLoginExistsException(userDto.getLogin());
|
||||
}
|
||||
if (getUserByEmail(userDto.getEmail()) != null) {
|
||||
throw new UserEmailExistsException(userDto.getEmail());
|
||||
}
|
||||
if (!userDto.isPasswordsValid()) {
|
||||
throw new UserPasswordsNotValidOrNotMatchException();
|
||||
}
|
||||
User user = userMapper.userDtoToUserEntity(userDto);
|
||||
user.setActivated(false);
|
||||
user.setActivationKey(UserUtils.generateActivationKey());
|
||||
user.setRoles(Collections.singleton(new UserRole(UserRoleConstants.USER)));
|
||||
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
|
||||
user = userRepository.save(user);
|
||||
mailService.sendActivationEmail(user);
|
||||
log.debug("Created Information for User: {}", user.getLogin());
|
||||
return userMapper.userEntityToUserDto(user);
|
||||
}
|
||||
|
||||
public UserDto activateUser(String activationKey) {
|
||||
final User user = getUserByActivationKey(activationKey);
|
||||
if (user == null) {
|
||||
throw new UserActivationError(activationKey);
|
||||
}
|
||||
user.setActivated(true);
|
||||
user.setActivationKey(null);
|
||||
user.setActivationDate(null);
|
||||
log.debug("Activated user: {}", user.getLogin());
|
||||
return userMapper.userEntityToUserDto(userRepository.save(user));
|
||||
}
|
||||
|
||||
public UserDto updateUser(UserDto userDto) {
|
||||
if (userDto.getId() == null) {
|
||||
throw new EntityIdIsNullException();
|
||||
}
|
||||
if (!Objects.equals(
|
||||
Optional.ofNullable(getUserByEmail(userDto.getEmail()))
|
||||
.map(BaseEntity::getId).orElse(userDto.getId()),
|
||||
userDto.getId())) {
|
||||
throw new UserEmailExistsException(userDto.getEmail());
|
||||
}
|
||||
if (!Objects.equals(
|
||||
Optional.ofNullable(getUserByLogin(userDto.getLogin()))
|
||||
.map(BaseEntity::getId).orElse(userDto.getId()),
|
||||
userDto.getId())) {
|
||||
throw new UserLoginExistsException(userDto.getLogin());
|
||||
}
|
||||
User user = userRepository.findOne(userDto.getId());
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException(userDto.getId().toString());
|
||||
}
|
||||
if (applicationProperties.getUndeadUserLogin().equalsIgnoreCase(user.getLogin())) {
|
||||
userDto.setLogin(applicationProperties.getUndeadUserLogin());
|
||||
userDto.setActivated(true);
|
||||
userDto.setRoles(Collections.singletonList(new UserRoleDto(UserRoleConstants.ADMIN)));
|
||||
}
|
||||
user.setLogin(userDto.getLogin());
|
||||
user.setFirstName(userDto.getFirstName());
|
||||
user.setLastName(userDto.getLastName());
|
||||
user.setEmail(userDto.getEmail());
|
||||
if (userDto.isActivated() != user.getActivated()) {
|
||||
if (userDto.isActivated()) {
|
||||
user.setActivationKey(null);
|
||||
user.setActivationDate(null);
|
||||
} else {
|
||||
user.setActivationKey(UserUtils.generateActivationKey());
|
||||
user.setActivationDate(new Date());
|
||||
}
|
||||
}
|
||||
user.setActivated(userDto.isActivated());
|
||||
final Set<UserRole> roles = userMapper.rolesFromDto(userDto.getRoles());
|
||||
user.setRoles(roles.isEmpty()
|
||||
? Collections.singleton(new UserRole(UserRoleConstants.USER))
|
||||
: roles);
|
||||
if (!StringUtils.isEmpty(userDto.getOldPassword())) {
|
||||
if (!userDto.isPasswordsValid() || !userDto.isOldPasswordValid()) {
|
||||
throw new UserPasswordsNotValidOrNotMatchException();
|
||||
}
|
||||
if (!passwordEncoder.matches(userDto.getOldPassword(), user.getPassword())) {
|
||||
throw new UserPasswordsNotValidOrNotMatchException();
|
||||
}
|
||||
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
|
||||
log.debug("Changed password for User: {}", user.getLogin());
|
||||
}
|
||||
user = userRepository.save(user);
|
||||
log.debug("Changed Information for User: {}", user.getLogin());
|
||||
return userMapper.userEntityToUserDto(user);
|
||||
}
|
||||
|
||||
public UserDto updateUserInformation(UserDto userDto) {
|
||||
if (userDto.getId() == null) {
|
||||
throw new EntityIdIsNullException();
|
||||
}
|
||||
if (!Objects.equals(
|
||||
Optional.ofNullable(getUserByEmail(userDto.getEmail()))
|
||||
.map(BaseEntity::getId).orElse(userDto.getId()),
|
||||
userDto.getId())) {
|
||||
throw new UserEmailExistsException(userDto.getEmail());
|
||||
}
|
||||
User user = userRepository.findOne(userDto.getId());
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException(userDto.getId().toString());
|
||||
}
|
||||
user.setFirstName(userDto.getFirstName());
|
||||
user.setLastName(userDto.getLastName());
|
||||
user.setEmail(userDto.getEmail());
|
||||
user = userRepository.save(user);
|
||||
log.debug("Updated Information for User: {}", user.getLogin());
|
||||
return userMapper.userEntityToUserDto(user);
|
||||
}
|
||||
|
||||
public UserDto changeUserPassword(UserDto userDto) {
|
||||
if (userDto.getId() == null) {
|
||||
throw new EntityIdIsNullException();
|
||||
}
|
||||
if (!userDto.isPasswordsValid() || !userDto.isOldPasswordValid()) {
|
||||
throw new UserPasswordsNotValidOrNotMatchException();
|
||||
}
|
||||
final String login = UserUtils.getCurrentUserLogin();
|
||||
final User user = userRepository.findOneByLoginIgnoreCase(login);
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException(login);
|
||||
}
|
||||
if (!passwordEncoder.matches(userDto.getOldPassword(), user.getPassword())) {
|
||||
throw new UserPasswordsNotValidOrNotMatchException();
|
||||
}
|
||||
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
|
||||
log.debug("Changed password for User: {}", user.getLogin());
|
||||
return userMapper.userEntityToUserDto(userRepository.save(user));
|
||||
}
|
||||
|
||||
public boolean requestUserPasswordReset(String email) {
|
||||
User user = userRepository.findOneByEmailIgnoreCase(email);
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException(email);
|
||||
}
|
||||
if (!user.getActivated()) {
|
||||
throw new UserNotActivatedException();
|
||||
}
|
||||
user.setResetKey(UserUtils.generateResetKey());
|
||||
user.setResetDate(new Date());
|
||||
user = userRepository.save(user);
|
||||
mailService.sendPasswordResetMail(user);
|
||||
log.debug("Created Reset Password Request for User: {}", user.getLogin());
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean completeUserPasswordReset(String key, UserResetPasswordDto userResetPasswordDto) {
|
||||
if (!userResetPasswordDto.isPasswordsValid()) {
|
||||
throw new UserPasswordsNotValidOrNotMatchException();
|
||||
}
|
||||
User user = userRepository.findOneByResetKey(key);
|
||||
if (user == null) {
|
||||
throw new UserResetKeyError(key);
|
||||
}
|
||||
user.setPassword(passwordEncoder.encode(userResetPasswordDto.getPassword()));
|
||||
user.setResetKey(null);
|
||||
user.setResetDate(null);
|
||||
user = userRepository.save(user);
|
||||
log.debug("Reset Password for User: {}", user.getLogin());
|
||||
return true;
|
||||
}
|
||||
|
||||
public UserDto deleteUser(Integer userId) {
|
||||
final User user = userRepository.findOne(userId);
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException(userId.toString());
|
||||
}
|
||||
if (applicationProperties.getUndeadUserLogin().equalsIgnoreCase(user.getLogin())) {
|
||||
throw new UserIsUndeadException(user.getLogin());
|
||||
}
|
||||
userRepository.delete(user);
|
||||
log.debug("Deleted User: {}", user.getLogin());
|
||||
return userMapper.userEntityToUserDto(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) {
|
||||
final User user = userRepository.findOneByLoginIgnoreCase(username);
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException(username);
|
||||
}
|
||||
if (!user.getActivated()) {
|
||||
throw new UserNotActivatedException();
|
||||
}
|
||||
return new org.springframework.security.core.userdetails.User(user.getLogin(),
|
||||
user.getPassword(),
|
||||
Optional.ofNullable(user.getRoles()).orElse(Collections.emptySet()).stream()
|
||||
.map(role -> new SimpleGrantedAuthority(role.getName()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
57
src/main/java/ru/ulstu/user/service/UserSessionService.java
Normal file
57
src/main/java/ru/ulstu/user/service/UserSessionService.java
Normal file
@ -0,0 +1,57 @@
|
||||
package ru.ulstu.user.service;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.ulstu.core.model.response.PageableItems;
|
||||
import ru.ulstu.core.jpa.OffsetablePageRequest;
|
||||
import ru.ulstu.user.error.UserNotFoundException;
|
||||
import ru.ulstu.user.model.User;
|
||||
import ru.ulstu.user.model.UserSession;
|
||||
import ru.ulstu.user.model.UserSessionListDto;
|
||||
import ru.ulstu.user.repository.UserSessionRepository;
|
||||
|
||||
import static ru.ulstu.core.util.StreamApiUtils.convert;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class UserSessionService {
|
||||
private final Logger log = LoggerFactory.getLogger(UserSessionService.class);
|
||||
private final UserSessionRepository userSessionRepository;
|
||||
private final UserService userService;
|
||||
|
||||
public UserSessionService(UserSessionRepository userSessionRepository, UserService userService) {
|
||||
this.userSessionRepository = userSessionRepository;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public PageableItems<UserSessionListDto> getSessions(int offset, int count) {
|
||||
final Page<UserSession> page = userSessionRepository.findAll(
|
||||
new OffsetablePageRequest(offset, count, new Sort(Sort.Direction.DESC, "loginTime")));
|
||||
return new PageableItems<>(page.getTotalElements(),
|
||||
convert(page.getContent(), UserSessionListDto::new));
|
||||
}
|
||||
|
||||
public void createUserSession(String sessionId, String login, String ipAddress, String host) {
|
||||
final User user = userService.getUserByLogin(login);
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException(login);
|
||||
}
|
||||
userSessionRepository.save(new UserSession(sessionId, ipAddress, host, user));
|
||||
log.debug("User session {} created for user {}@{} ({})", sessionId, login, ipAddress, host);
|
||||
}
|
||||
|
||||
public void closeUserSession(String sessionId) {
|
||||
final UserSession userSession = userSessionRepository.findOneBySessionId(sessionId);
|
||||
if (userSession == null) {
|
||||
throw new IllegalArgumentException(String.format("User session %s not found", sessionId));
|
||||
}
|
||||
userSession.close();
|
||||
userSessionRepository.save(userSession);
|
||||
log.debug("User session {} closed", sessionId);
|
||||
}
|
||||
}
|
35
src/main/java/ru/ulstu/user/util/UserUtils.java
Normal file
35
src/main/java/ru/ulstu/user/util/UserUtils.java
Normal file
@ -0,0 +1,35 @@
|
||||
package ru.ulstu.user.util;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
public class UserUtils {
|
||||
private static final int DEF_COUNT = 20;
|
||||
|
||||
public static String generateActivationKey() {
|
||||
return RandomStringUtils.randomNumeric(DEF_COUNT);
|
||||
}
|
||||
|
||||
public static String generateResetKey() {
|
||||
return RandomStringUtils.randomNumeric(DEF_COUNT);
|
||||
}
|
||||
|
||||
public static String getCurrentUserLogin() {
|
||||
final SecurityContext securityContext = SecurityContextHolder.getContext();
|
||||
if (securityContext == null) {
|
||||
return null;
|
||||
}
|
||||
final Authentication authentication = securityContext.getAuthentication();
|
||||
if (authentication.getPrincipal() instanceof UserDetails) {
|
||||
final UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
|
||||
return springSecurityUser.getUsername();
|
||||
}
|
||||
if (authentication.getPrincipal() instanceof String) {
|
||||
return (String) authentication.getPrincipal();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
36
src/main/resources/application.properties
Normal file
36
src/main/resources/application.properties
Normal file
@ -0,0 +1,36 @@
|
||||
# Server Settings
|
||||
spring.main.banner-mode=off
|
||||
server.port=8443
|
||||
server.http.port=8080
|
||||
spring.http.multipart.maxFileSize=20MB
|
||||
spring.http.multipart.maxRequestSize=20MB
|
||||
# Thymeleaf Settings
|
||||
spring.thymeleaf.cache=false
|
||||
# SSL Settings
|
||||
security.require-ssl=true
|
||||
server.ssl.key-store=classpath:sample.jks
|
||||
server.ssl.key-store-password=secret
|
||||
server.ssl.key-password=password
|
||||
# Log settings (TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF)
|
||||
logging.level.ru.ulstu=DEBUG
|
||||
# Mail Settings
|
||||
spring.mail.host=smtp.yandex.ru
|
||||
spring.mail.port=465
|
||||
spring.mail.username=balance@soft.kitchen
|
||||
spring.mail.password=fkvfpbalance
|
||||
spring.mail.properties.mail.smtp.auth=true
|
||||
spring.mail.properties.mail.smtp.ssl.enable=true
|
||||
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
|
||||
# JPA Settings
|
||||
spring.datasource.url=jdbc:postgresql://localhost:5432/ng-tracker
|
||||
spring.datasource.username=postgres
|
||||
spring.datasource.password=postgres
|
||||
spring.datasource.driverclassName=org.postgresql.Driver
|
||||
spring.jpa.hibernate.ddl-auto=validate
|
||||
# Liquibase Settings
|
||||
liquibase.drop-first=false
|
||||
liquibase.enabled=true
|
||||
liquibase.change-log=classpath:db/changelog-master.xml
|
||||
# Application Settings
|
||||
ng-tracker.base-url=https://127.0.0.1:8443
|
||||
ng-tracker.undead-user-login=admin
|
489
src/main/resources/commits.log
Normal file
489
src/main/resources/commits.log
Normal file
@ -0,0 +1,489 @@
|
||||
Anton Romanov;Thu Mar 15 11:10:34 2018 +0400;change to date time
|
||||
romanov73;Thu Mar 15 11:03:19 2018 +0400;read commits in constructor
|
||||
romanov73;Thu Mar 15 09:12:07 2018 +0400;add commits page
|
||||
Romanov Anton;Wed Mar 14 20:26:54 2018 +0000;Update README.md
|
||||
Romanov Anton;Wed Mar 14 20:16:53 2018 +0000;Update README.md
|
||||
Romanov Anton;Wed Mar 14 20:08:09 2018 +0000;Update README.md
|
||||
Romanov Anton;Wed Mar 14 20:00:58 2018 +0000;Update README.md
|
||||
Romanov Anton;Wed Mar 14 19:50:22 2018 +0000;Update README.md
|
||||
Romanov Anton;Wed Mar 14 19:34:17 2018 +0000;Update README.md
|
||||
Romanov Anton;Wed Mar 14 19:20:42 2018 +0000;Update README.md
|
||||
Romanov Anton;Wed Mar 14 18:59:18 2018 +0000;Update README.md
|
||||
Romanov Anton;Wed Mar 14 18:47:17 2018 +0000;Update README.md
|
||||
Romanov Anton;Wed Mar 14 18:35:37 2018 +0000;Update README.md
|
||||
Romanov Anton;Wed Mar 14 18:33:54 2018 +0000;Update README.md
|
||||
Romanov Anton;Wed Mar 14 18:29:27 2018 +0000;Update README.md
|
||||
Romanov Anton;Wed Mar 14 18:21:18 2018 +0000;Update README.md
|
||||
Romanov Anton;Wed Mar 14 18:19:27 2018 +0000;Update README.md
|
||||
Romanov Anton;Wed Mar 14 18:16:06 2018 +0000;Update README.md
|
||||
romanov73;Wed Mar 14 21:28:10 2018 +0400;fix using constant
|
||||
Aleksey Filippov;Wed Mar 14 20:07:25 2018 +0400;some fixes after merge
|
||||
Aleksey Filippov;Wed Mar 14 19:52:45 2018 +0400;Merge remote-tracking branch 'origin/36-rest' into odin-ui
|
||||
Aleksey Filippov;Wed Mar 14 19:48:45 2018 +0400;refactoring of odin paginator
|
||||
romanov73;Wed Mar 14 18:43:16 2018 +0400;Merge branch 'develop' into 36-rest
|
||||
romanov73;Wed Mar 14 18:17:02 2018 +0400;Merge branch 'develop' into 36-rest
|
||||
Aleksey Filippov;Wed Mar 14 18:11:00 2018 +0400;fix null value check in formatter
|
||||
Aleksey Filippov;Wed Mar 14 18:06:16 2018 +0400;fix odin paginator style, fix odin negative file
|
||||
romanov73;Wed Mar 14 18:04:47 2018 +0400;rename entities
|
||||
Aleksey Filippov;Wed Mar 14 17:58:59 2018 +0400;improve odin table look
|
||||
romanov73;Wed Mar 14 17:47:45 2018 +0400;fix db changelogs
|
||||
romanov73;Wed Mar 14 17:47:20 2018 +0400;fix db changelogs
|
||||
Gleb;Wed Mar 14 08:50:23 2018 +0000;Merge branch '48-' into 'develop'
|
||||
funny73;Wed Mar 14 12:28:55 2018 +0400;Поправил отображение подразделения при редактировании смены
|
||||
Aleksey Filippov;Wed Mar 14 00:02:24 2018 +0400;move navbar to the left, move odin css to separate file
|
||||
Aleksey Filippov;Tue Mar 13 23:26:33 2018 +0400;some style fixes
|
||||
Aleksey Filippov;Tue Mar 13 23:22:37 2018 +0400;move version panel to navbar
|
||||
funny73;Tue Mar 13 20:41:41 2018 +0400;Поправил валидацию сменности
|
||||
romanov73;Tue Mar 13 17:26:54 2018 +0400;fix offsetable page request
|
||||
romanov73;Tue Mar 13 17:26:02 2018 +0400;fix table constructor
|
||||
romanov73;Tue Mar 13 17:24:56 2018 +0400;fix checking empty value
|
||||
Romanov Anton;Tue Mar 13 13:09:41 2018 +0000;Merge branch 'odin-ui' into '36-rest'
|
||||
Aleksey Filippov;Tue Mar 13 16:59:27 2018 +0400;notes updated
|
||||
Aleksey Filippov;Tue Mar 13 16:56:00 2018 +0400;odin refactoring
|
||||
Aleksey Filippov;Tue Mar 13 16:53:38 2018 +0400;odin refactoring
|
||||
romanov73;Tue Mar 13 15:27:17 2018 +0400;fix tool load calc
|
||||
romanov73;Tue Mar 13 14:46:46 2018 +0400;fix area load calc
|
||||
Aleksey Filippov;Tue Mar 13 13:38:23 2018 +0400;some fixes
|
||||
romanov73;Tue Mar 13 13:33:25 2018 +0400;Merge remote-tracking branch 'origin/develop' into develop
|
||||
romanov73;Tue Mar 13 13:33:07 2018 +0400;fix tools count calc
|
||||
Aleksey Filippov;Tue Mar 13 13:12:09 2018 +0400;some odin refactoring
|
||||
Aleksey Filippov;Tue Mar 13 13:11:41 2018 +0400;some offsetablepagerequest fix
|
||||
Aleksey Filippov;Tue Mar 13 13:06:48 2018 +0400;add offsetablepagerequest
|
||||
Aleksey Filippov;Tue Mar 13 07:27:12 2018 +0000;Merge branch '51-rest-points' into '36-rest'
|
||||
romanov73;Tue Mar 13 11:22:02 2018 +0400;fix by comment: remove default version id
|
||||
romanov73;Tue Mar 13 00:04:50 2018 +0400;fix path
|
||||
romanov73;Mon Mar 12 23:55:05 2018 +0400;add tool square dictionary
|
||||
romanov73;Mon Mar 12 23:36:49 2018 +0400;add dictionary pages
|
||||
romanov73;Mon Mar 12 23:18:01 2018 +0400;sort second level menu items by name
|
||||
romanov73;Mon Mar 12 23:11:22 2018 +0400;fix menu
|
||||
romanov73;Mon Mar 12 22:52:15 2018 +0400;add stream api utils and converter for "map ... collect"
|
||||
funny73;Mon Mar 12 20:01:47 2018 +0400;Добавил свойство базового изделия для изделия
|
||||
romanov73;Mon Mar 12 19:46:52 2018 +0400;refactor units
|
||||
romanov73;Mon Mar 12 19:36:57 2018 +0400;refactor tool types and work types
|
||||
romanov73;Mon Mar 12 19:20:23 2018 +0400;refactor tools
|
||||
romanov73;Mon Mar 12 19:07:09 2018 +0400;refactor stages
|
||||
romanov73;Mon Mar 12 18:53:26 2018 +0400;refactor positions
|
||||
romanov73;Mon Mar 12 18:27:36 2018 +0400;refactor employees
|
||||
romanov73;Mon Mar 12 18:26:49 2018 +0400;refactor employees
|
||||
romanov73;Mon Mar 12 18:09:38 2018 +0400;refactor categories
|
||||
Aleksey Filippov;Mon Mar 12 17:32:06 2018 +0400;todo added
|
||||
Aleksey Filippov;Mon Mar 12 17:29:51 2018 +0400;paginator added to odin
|
||||
Aleksey Filippov;Mon Mar 12 17:29:06 2018 +0400;notes updated
|
||||
funny73;Mon Mar 12 16:29:13 2018 +0400;Добавил сущность изделие без привязки к производственной программе
|
||||
funny73;Mon Mar 12 15:16:35 2018 +0400;Переименовал сущность Product на ProductOnProgram Ещё немного переименования
|
||||
funny73;Mon Mar 12 14:39:52 2018 +0400;Переименовал сущность Product на ProductOnProgram
|
||||
Aleksey Filippov;Mon Mar 12 13:44:42 2018 +0400;add formatters and initial form support to odin
|
||||
Aleksey Filippov;Mon Mar 12 13:44:16 2018 +0400;userlistdto refactoring
|
||||
Aleksey Filippov;Mon Mar 12 13:43:59 2018 +0400;dateutils improvements
|
||||
Aleksey Filippov;Mon Mar 12 13:43:43 2018 +0400;add support of localdatetime type to odin
|
||||
Aleksey Filippov;Mon Mar 12 13:42:54 2018 +0400;odin backend example added
|
||||
Aleksey Filippov;Mon Mar 12 13:42:12 2018 +0400;notes updated
|
||||
funny73;Mon Mar 12 12:16:02 2018 +0400;Поправил всплывающие сообщения при работе с "Категориями"
|
||||
Aleksey Filippov;Mon Mar 12 11:25:51 2018 +0400;some refactoring
|
||||
Aleksey Filippov;Mon Mar 12 11:25:30 2018 +0400;odinid annotation added
|
||||
Aleksey Filippov;Sun Mar 11 15:23:49 2018 +0400;notes updated
|
||||
Aleksey Filippov;Sun Mar 11 15:18:45 2018 +0400;some fixes
|
||||
Aleksey Filippov;Sun Mar 11 15:09:57 2018 +0400;simple table draw support added, some refactoring
|
||||
Aleksey Filippov;Sun Mar 11 15:08:22 2018 +0400;add user id field to userlistdto
|
||||
Aleksey Filippov;Sun Mar 11 14:32:50 2018 +0400;add jsonproperty annotation support
|
||||
Aleksey Filippov;Sun Mar 11 13:38:45 2018 +0400;some template fixes
|
||||
Aleksey Filippov;Sun Mar 11 13:33:09 2018 +0400;some balance page fixes
|
||||
Aleksey Filippov;Sun Mar 11 13:25:58 2018 +0400;add jsonignore annotation support
|
||||
Aleksey Filippov;Thu Mar 8 14:16:20 2018 +0400;some odin improvements
|
||||
Aleksey Filippov;Wed Mar 7 16:03:04 2018 +0400;Merge remote-tracking branch 'origin/36-rest' into 36-rest
|
||||
Aleksey Filippov;Wed Mar 7 16:01:21 2018 +0400;odin submodule for basic types added, some refactoring
|
||||
romanov73;Tue Mar 6 22:52:02 2018 +0400;remove old packages
|
||||
romanov73;Tue Mar 6 19:12:27 2018 +0400;add tool type crud
|
||||
romanov73;Tue Mar 6 13:09:51 2018 +0400;add version crud
|
||||
romanov73;Tue Mar 6 10:48:36 2018 +0400;add unit crud
|
||||
romanov73;Mon Mar 5 16:58:37 2018 +0400;fix tree
|
||||
romanov73;Mon Mar 5 16:38:37 2018 +0400;Merge remote-tracking branch 'origin/36-rest' into 36-rest
|
||||
romanov73;Mon Mar 5 16:38:21 2018 +0400;employee crud
|
||||
Aleksey Filippov;Mon Mar 5 15:43:49 2018 +0400;some fixes
|
||||
Aleksey Filippov;Mon Mar 5 15:39:53 2018 +0400;Merge remote-tracking branch 'origin/36-rest' into 36-rest
|
||||
Aleksey Filippov;Mon Mar 5 15:39:33 2018 +0400;user reset password function added, some refactoring
|
||||
romanov73;Mon Mar 5 15:29:16 2018 +0400;fix delete category
|
||||
Aleksey Filippov;Mon Mar 5 15:08:27 2018 +0400;user change password function added, some refactoring
|
||||
romanov73;Mon Mar 5 15:08:17 2018 +0400;add category crud
|
||||
romanov73;Mon Mar 5 14:07:10 2018 +0400;add tree component
|
||||
Aleksey Filippov;Mon Mar 5 11:30:22 2018 +0400;user delete function added, some refactoring
|
||||
Aleksey Filippov;Mon Mar 5 11:18:23 2018 +0400;user update function added
|
||||
Aleksey Filippov;Mon Mar 5 11:18:06 2018 +0400;some refactoring
|
||||
Aleksey Filippov;Mon Mar 5 10:06:04 2018 +0400;add user activation function
|
||||
Aleksey Filippov;Mon Mar 5 09:48:22 2018 +0400;add scheduler for users
|
||||
Aleksey Filippov;Mon Mar 5 09:48:07 2018 +0400;add activateddate field to userentity
|
||||
Aleksey Filippov;Mon Mar 5 09:47:25 2018 +0400;notes file updated
|
||||
Aleksey Filippov;Mon Mar 5 09:47:11 2018 +0400;some refactoring
|
||||
Aleksey Filippov;Mon Mar 5 09:21:49 2018 +0400;thymeleaf cache settings added
|
||||
Aleksey Filippov;Mon Mar 5 09:21:11 2018 +0400;notes file added
|
||||
Aleksey Filippov;Mon Mar 5 09:20:57 2018 +0400;some refactoring
|
||||
Aleksey Filippov;Mon Mar 5 08:37:01 2018 +0400;some refactoring
|
||||
romanov73;Mon Mar 5 00:08:02 2018 +0400;add callbacks on version getters
|
||||
romanov73;Mon Mar 5 00:07:34 2018 +0400;fix npe
|
||||
romanov73;Sun Mar 4 01:13:57 2018 +0400;fix column style
|
||||
romanov73;Sun Mar 4 01:06:38 2018 +0400;fix table style
|
||||
romanov73;Sun Mar 4 00:27:14 2018 +0400;add balance dto + draw employee balance
|
||||
romanov73;Sat Mar 3 22:43:06 2018 +0400;add balance services
|
||||
romanov73;Sat Mar 3 16:31:19 2018 +0400;Merge remote-tracking branch 'origin/36-rest' into 36-rest
|
||||
romanov73;Sat Mar 3 16:31:04 2018 +0400;add employee balance table
|
||||
Aleksey Filippov;Sat Mar 3 15:54:23 2018 +0400;some user support improvements
|
||||
Aleksey Filippov;Sat Mar 3 15:53:20 2018 +0400;edit migration files, need manual fix of databasechangelog table
|
||||
Aleksey Filippov;Sat Mar 3 15:51:58 2018 +0400;add application properties handler
|
||||
Aleksey Filippov;Sat Mar 3 15:51:19 2018 +0400;disable tests
|
||||
Aleksey Filippov;Sat Mar 3 13:49:32 2018 +0400;some refactoring
|
||||
romanov73;Fri Mar 2 18:10:23 2018 +0400;add balance page
|
||||
Aleksey Filippov;Fri Mar 2 18:04:39 2018 +0400;some refactoring
|
||||
Aleksey Filippov;Fri Mar 2 17:51:41 2018 +0400;Merge remote-tracking branch 'origin/36-rest' into 36-rest
|
||||
Aleksey Filippov;Fri Mar 2 17:50:59 2018 +0400;advicecontroller improvements
|
||||
Aleksey Filippov;Fri Mar 2 17:50:39 2018 +0400;some mvc improvements
|
||||
romanov73;Fri Mar 2 15:15:46 2018 +0400;add dtos
|
||||
romanov73;Fri Mar 2 15:02:38 2018 +0400;Merge remote-tracking branch 'origin/36-rest' into 36-rest
|
||||
Aleksey Filippov;Fri Mar 2 14:54:33 2018 +0400;Merge remote-tracking branch 'origin/36-rest' into 36-rest
|
||||
Aleksey Filippov;Fri Mar 2 14:53:39 2018 +0400;add migrations
|
||||
Aleksey Filippov;Fri Mar 2 14:53:30 2018 +0400;some core improvements
|
||||
Aleksey Filippov;Fri Mar 2 14:53:13 2018 +0400;some users improvements
|
||||
romanov73;Fri Mar 2 12:42:54 2018 +0400;add category service
|
||||
romanov73;Fri Mar 2 12:37:51 2018 +0400;fix unit service
|
||||
romanov73;Fri Mar 2 11:24:11 2018 +0400;add unit controller
|
||||
romanov73;Thu Mar 1 23:19:42 2018 +0400;fix versions panel
|
||||
romanov73;Thu Mar 1 22:50:57 2018 +0400;Merge branch 'develop' into 36-rest
|
||||
romanov73;Thu Mar 1 22:50:37 2018 +0400;Merge branch 'develop' into 36-rest
|
||||
Romanov Anton;Thu Mar 1 18:31:03 2018 +0000;Merge branch '29-' into 'develop'
|
||||
romanov73;Thu Mar 1 22:25:49 2018 +0400;change versions
|
||||
romanov73;Thu Mar 1 20:14:22 2018 +0400;show version select
|
||||
romanov73;Thu Mar 1 19:56:24 2018 +0400;add old models, add versions controller
|
||||
romanov73;Thu Mar 1 19:55:59 2018 +0400;add old models, add versions controller
|
||||
romanov73;Thu Mar 1 19:04:47 2018 +0400;save menu to session
|
||||
romanov73;Thu Mar 1 18:50:00 2018 +0400;add favicon
|
||||
funny73;Thu Mar 1 18:01:43 2018 +0400;1) Поправил имена колонок и таблицы для смен под постгрес 2) Исправил ошибку в имени переменной thidShift ->thirdShift
|
||||
romanov73;Thu Mar 1 16:03:03 2018 +0400;add menu and restore changelogs
|
||||
funny73;Thu Mar 1 15:33:36 2018 +0400;Добавил распорядок смен для каждого подразделения. Смена назначается на месяц. Есть возможность сохранить любой вариант комбинирования 1,2 и 3 смены. Например (1,3); (2); ()... Добавлен интерфейс для просмотра и редактирования распорядка смен по каждому подразделению. Добавлена валидация - для каждого месяца может быть только один распорядок смен.
|
||||
Aleksey Filippov;Thu Mar 1 15:23:06 2018 +0400;migrate to webjars
|
||||
Aleksey Filippov;Thu Mar 1 13:31:16 2018 +0400;Merge remote-tracking branch 'origin/36-rest' into 36-rest
|
||||
Aleksey Filippov;Thu Mar 1 13:30:55 2018 +0400;create migrations for user models
|
||||
Aleksey Filippov;Thu Mar 1 13:30:09 2018 +0400;old code refactoring
|
||||
romanov73;Thu Mar 1 12:51:14 2018 +0400;add basic menu from rest
|
||||
romanov73;Thu Mar 1 11:51:17 2018 +0400;Merge remote-tracking branch 'origin/36-rest' into 36-rest
|
||||
romanov73;Thu Mar 1 11:50:59 2018 +0400;try to fix ci: 3 remove tests
|
||||
Aleksey Filippov;Thu Mar 1 11:50:25 2018 +0400;Merge remote-tracking branch 'origin/36-rest' into 36-rest
|
||||
Aleksey Filippov;Thu Mar 1 11:49:59 2018 +0400;moved to new database
|
||||
romanov73;Thu Mar 1 11:45:40 2018 +0400;try to fix ci: 2 change image
|
||||
romanov73;Thu Mar 1 11:44:42 2018 +0400;try to fix ci: 1
|
||||
romanov73;Thu Mar 1 11:15:52 2018 +0400;fix csrf tokens
|
||||
Aleksey Filippov;Thu Mar 1 00:25:43 2018 +0400;initial user management support added
|
||||
romanov73;Wed Feb 28 22:26:00 2018 +0400;save current version in local storage
|
||||
romanov73;Wed Feb 28 18:40:57 2018 +0400;add ajax datatable
|
||||
romanov73;Wed Feb 28 18:01:23 2018 +0400;add static index page with menu
|
||||
Romanov Anton;Tue Feb 27 16:53:04 2018 +0000;Merge branch '33-' into 'develop'
|
||||
romanov73;Tue Feb 27 20:50:44 2018 +0400;calc area balance
|
||||
romanov73;Tue Feb 27 17:36:31 2018 +0400;remove unused panel
|
||||
Romanov Anton;Tue Feb 27 13:06:02 2018 +0000;Merge branch '32-' into 'develop'
|
||||
romanov73;Tue Feb 27 17:02:35 2018 +0400;calc tool balance
|
||||
romanov73;Tue Feb 27 15:45:39 2018 +0400;calc tool balance
|
||||
romanov73;Tue Feb 27 12:16:41 2018 +0400;calc tool power
|
||||
romanov73;Tue Feb 27 11:40:59 2018 +0400;use tool types in dto
|
||||
romanov73;Tue Feb 27 11:29:05 2018 +0400;fix dtos
|
||||
romanov73;Mon Feb 26 23:50:53 2018 +0400;inherit dtos
|
||||
romanov73;Mon Feb 26 15:38:00 2018 +0400;fix services for balance calculation
|
||||
Aleksey Filippov;Mon Feb 26 15:00:48 2018 +0400;move from maven to gradle, move from javaee to spring boot
|
||||
romanov73;Thu Feb 22 18:03:35 2018 +0400;Add menu resource
|
||||
Romanov Anton;Tue Feb 20 18:12:55 2018 +0000;Merge branch 'balance-different-dto' into 'develop'
|
||||
romanov73;Tue Feb 20 22:09:33 2018 +0400;restore tests
|
||||
romanov73;Tue Feb 20 21:46:45 2018 +0400;filter employees by stage
|
||||
romanov73;Tue Feb 20 21:21:27 2018 +0400;filter employees by workload
|
||||
romanov73;Tue Feb 20 20:58:23 2018 +0400;Merge branch 'develop' into balance-different-dto
|
||||
funny73;Tue Feb 20 19:52:24 2018 +0400;Add shift unit model Add link to unit view
|
||||
romanov73;Tue Feb 20 16:14:09 2018 +0400;Important fix using coefficient
|
||||
romanov73;Mon Feb 19 19:00:07 2018 +0400;partially fix ajustment
|
||||
romanov73;Mon Feb 19 18:24:10 2018 +0400;Modify dto for using with different periods and work types
|
||||
Romanov Anton;Mon Feb 19 08:11:39 2018 +0000;Merge branch '30-' into 'develop'
|
||||
romanov73;Mon Feb 19 12:09:05 2018 +0400;Add work type to position
|
||||
Romanov Anton;Wed Feb 14 20:57:35 2018 +0000;Merge branch '34-' into 'develop'
|
||||
romanov73;Thu Feb 15 00:51:24 2018 +0400;fixes by comments
|
||||
romanov73;Thu Feb 15 00:47:21 2018 +0400;merge fields
|
||||
romanov73;Wed Feb 14 23:42:35 2018 +0400;Merge branch 'develop' into 34-gleb
|
||||
romanov73;Wed Feb 14 21:05:51 2018 +0400;add clock
|
||||
Romanov Anton;Wed Feb 14 16:49:29 2018 +0000;Merge branch '45-balance-employee-recomendations' into 'develop'
|
||||
romanov73;Wed Feb 14 20:46:59 2018 +0400;cleanup code
|
||||
romanov73;Wed Feb 14 18:25:42 2018 +0400;reduce code
|
||||
romanov73;Wed Feb 14 18:24:59 2018 +0400;adjust additional employees
|
||||
romanov73;Wed Feb 14 17:22:10 2018 +0400;move employees from other units
|
||||
romanov73;Tue Feb 13 23:59:09 2018 +0400;fix save product name
|
||||
romanov73;Tue Feb 13 23:28:30 2018 +0400;add converter, fix UI and backing for add complex object
|
||||
romanov73;Tue Feb 13 22:10:59 2018 +0400;Merge branch 'develop' into 34-gleb
|
||||
funny73;Tue Feb 13 18:07:25 2018 +0400;Add stage to workload
|
||||
funny73;Tue Feb 13 16:05:45 2018 +0400;Add stage to category
|
||||
romanov73;Tue Feb 13 13:20:16 2018 +0400;add map of all units balances
|
||||
romanov73;Tue Feb 13 11:24:50 2018 +0400;move method
|
||||
romanov73;Tue Feb 13 00:35:30 2018 +0400;add dialog
|
||||
romanov73;Mon Feb 12 22:59:49 2018 +0400;filter only workshops and manufactures
|
||||
romanov73;Mon Feb 12 22:04:55 2018 +0400;rename employees
|
||||
romanov73;Mon Feb 12 17:34:37 2018 +0400;fix font size
|
||||
romanov73;Mon Feb 12 16:59:16 2018 +0400;fix versions panel width
|
||||
Romanov Anton;Mon Feb 12 12:47:04 2018 +0000;Merge branch '41-' into 'develop'
|
||||
romanov73;Mon Feb 12 16:43:06 2018 +0400;change to working hours
|
||||
romanov73;Mon Feb 12 14:05:36 2018 +0400;remove constructor
|
||||
romanov73;Mon Feb 12 14:05:13 2018 +0400;Merge remote-tracking branch 'origin/develop' into develop
|
||||
romanov73;Mon Feb 12 14:04:57 2018 +0400;fix selects
|
||||
Romanov Anton;Mon Feb 12 09:20:05 2018 +0000;Merge branch '39-work-type-code' into 'develop'
|
||||
Aleksey Filippov;Mon Feb 12 13:16:36 2018 +0400;add migration for work type code
|
||||
Aleksey Filippov;Mon Feb 12 13:16:28 2018 +0400;add work type code support to xhtml
|
||||
Aleksey Filippov;Mon Feb 12 13:16:12 2018 +0400;add work type code
|
||||
Romanov Anton;Mon Feb 12 08:25:47 2018 +0000;Update README.md
|
||||
Romanov Anton;Mon Feb 12 08:25:35 2018 +0000;Update README.md
|
||||
Romanov Anton;Mon Feb 12 08:07:30 2018 +0000;Update README.md
|
||||
Romanov Anton;Mon Feb 12 07:10:38 2018 +0000;Merge branch '40-' into 'develop'
|
||||
Aleksey Filippov;Mon Feb 12 11:08:13 2018 +0400;add migration for position type
|
||||
Aleksey Filippov;Mon Feb 12 11:07:45 2018 +0400;add position type support to xhtml
|
||||
Aleksey Filippov;Mon Feb 12 11:07:23 2018 +0400;move position converter from boundary to view
|
||||
Aleksey Filippov;Mon Feb 12 11:06:55 2018 +0400;add position type converter
|
||||
Aleksey Filippov;Mon Feb 12 11:06:35 2018 +0400;add position type to backing
|
||||
Aleksey Filippov;Mon Feb 12 11:06:00 2018 +0400;add position type support to service
|
||||
Aleksey Filippov;Mon Feb 12 11:05:44 2018 +0400;add position type to model
|
||||
Romanov Anton;Mon Feb 12 07:02:32 2018 +0000;Merge branch '33-' into 'develop'
|
||||
romanov73;Mon Feb 12 14:58:51 2018 +0400;refactor
|
||||
romanov73;Mon Feb 12 14:58:33 2018 +0400;add human hours coefficient
|
||||
romanov73;Mon Feb 12 14:56:43 2018 +0400;fix api name
|
||||
romanov73;Mon Feb 12 00:58:15 2018 +0400;add balance button
|
||||
romanov73;Sun Feb 11 01:36:15 2018 +0400;add tools balance prototype
|
||||
romanov73;Sat Feb 10 23:09:33 2018 +0400;move enum
|
||||
romanov73;Sat Feb 10 23:02:36 2018 +0400;refactor
|
||||
romanov73;Sat Feb 10 22:54:28 2018 +0400;add quarter
|
||||
romanov73;Sat Feb 10 22:42:14 2018 +0400;refactor
|
||||
romanov73;Sat Feb 10 20:52:46 2018 +0400;Merge branch 'develop' into 33-balance-area
|
||||
romanov73;Sat Feb 10 20:52:32 2018 +0400;Merge branch 'develop' into 33-balance-area
|
||||
Romanov Anton;Sat Feb 10 16:14:26 2018 +0000;Merge branch '31-map-2-dto' into 'develop'
|
||||
romanov73;Sat Feb 10 19:35:17 2018 +0400;fixes after Almaz consultation
|
||||
romanov73;Sat Feb 10 14:25:00 2018 +0400;fix npe
|
||||
romanov73;Sat Feb 10 13:50:21 2018 +0400;add filter by work type
|
||||
romanov73;Sat Feb 10 13:08:56 2018 +0400;rename field
|
||||
romanov73;Sat Feb 10 13:08:38 2018 +0400;filter by all children
|
||||
Aleksey Filippov;Sat Feb 10 11:26:17 2018 +0400;some fixes
|
||||
Aleksey Filippov;Sat Feb 10 11:26:06 2018 +0400;add dto for total balance
|
||||
Aleksey Filippov;Sat Feb 10 11:11:58 2018 +0400;add dto for employee load by unit
|
||||
Aleksey Filippov;Sat Feb 10 10:30:11 2018 +0400;getEmployeesByUnit method refactoring
|
||||
romanov73;Sat Feb 10 00:22:37 2018 +0400;add other type of balance area
|
||||
romanov73;Fri Feb 9 22:03:39 2018 +0400;add tools balance
|
||||
romanov73;Fri Feb 9 19:50:21 2018 +0400;divide balance page
|
||||
Aleksey Filippov;Fri Feb 9 16:37:18 2018 +0400;adapt backing and view to areas and employee experience dtos
|
||||
Aleksey Filippov;Fri Feb 9 16:36:18 2018 +0400;add dto for employee experience
|
||||
Aleksey Filippov;Fri Feb 9 16:35:51 2018 +0400;add dto for areas
|
||||
Aleksey Filippov;Fri Feb 9 14:57:01 2018 +0400;some ui fixes
|
||||
Aleksey Filippov;Fri Feb 9 14:55:36 2018 +0400;move db methods from getters to init
|
||||
romanov73;Thu Feb 8 01:20:12 2018 +0400;add areas panel
|
||||
romanov73;Thu Feb 8 01:19:13 2018 +0400;add areas panel
|
||||
romanov73;Thu Feb 8 01:02:39 2018 +0400;add global preloader
|
||||
romanov73;Thu Feb 8 00:57:46 2018 +0400;select default unit
|
||||
romanov73;Thu Feb 8 00:57:23 2018 +0400;add preloader
|
||||
romanov73;Wed Feb 7 22:46:44 2018 +0400;change id
|
||||
Romanov Anton;Wed Feb 7 13:53:35 2018 +0000;Merge branch '23-' into 'develop'
|
||||
Romanov Anton;Wed Feb 7 13:49:27 2018 +0000;Merge branch 'deploy-fixes' into 'develop'
|
||||
funny73;Wed Feb 7 17:34:41 2018 +0400;UDP modify AirplaneKitCounter to double
|
||||
Aleksey Filippov;Wed Feb 7 17:33:23 2018 +0400;some wildfly deploy fixes
|
||||
funny73;Wed Feb 7 17:19:22 2018 +0400;Modify AirplaneKitCounter to double -> Workload.java
|
||||
funny73;Wed Feb 7 17:00:10 2018 +0400;Add AirplaneKitCounter to balance view -> BalanceEmployeeService.java Modify Worckload total Value (Value * AirplaneKitCounter) -> BalanceService.java
|
||||
funny73;Wed Feb 7 14:13:35 2018 +0400;Add AirplaneKitCounter to view
|
||||
funny73;Wed Feb 7 14:13:15 2018 +0400;Add AirplaneKitCounter to model Workload
|
||||
funny73;Wed Feb 7 14:12:15 2018 +0400;Changelog for airplainetKitCounter
|
||||
Romanov Anton;Wed Feb 7 06:54:55 2018 +0000;Merge branch '22-' into 'develop'
|
||||
romanov73;Wed Feb 7 14:47:34 2018 +0400;add unit types
|
||||
Romanov Anton;Tue Feb 6 22:43:40 2018 +0000;Merge branch '25-' into 'develop'
|
||||
romanov73;Wed Feb 7 02:41:34 2018 +0400;filter balance by unit
|
||||
romanov73;Wed Feb 7 02:06:46 2018 +0400;filter balance by unit
|
||||
romanov73;Wed Feb 7 02:05:10 2018 +0400;filter balance by unit
|
||||
romanov73;Wed Feb 7 00:41:01 2018 +0400;add method get all unit children
|
||||
romanov73;Tue Feb 6 23:26:18 2018 +0400;add filter by unit in balance results
|
||||
romanov73;Tue Feb 6 21:26:45 2018 +0400;refactor work with tree of menu items
|
||||
romanov73;Tue Feb 6 21:08:01 2018 +0400;refactor work with tree
|
||||
romanov73;Tue Feb 6 21:07:22 2018 +0400;fix update after remove enum key
|
||||
romanov73;Tue Feb 6 20:03:31 2018 +0400;rename method
|
||||
Gleb;Tue Feb 6 15:07:04 2018 +0000;Merge branch '17-' into 'develop'
|
||||
romanov73;Tue Feb 6 21:15:12 2018 +0400;fix inf.
|
||||
funny73;Tue Feb 6 19:02:36 2018 +0400;Add changelog for units with unit_type == 'Корпус', unit_type='Цех'
|
||||
funny73;Tue Feb 6 18:19:50 2018 +0400;Remove "Korpus" from "Tip podrazdeleniya"
|
||||
Romanov Anton;Tue Feb 6 11:31:43 2018 +0000;Merge branch '24-' into 'develop'
|
||||
romanov73;Tue Feb 6 19:01:21 2018 +0400;add user
|
||||
Romanov Anton;Thu Feb 1 19:45:21 2018 +0000;Merge branch 'wildfly-deploy' into 'develop'
|
||||
Aleksey Filippov;Thu Feb 1 23:24:34 2018 +0400;add wildfly-maven-plugin, remove maven-glassfish-plugin
|
||||
romanov73;Thu Feb 1 19:51:24 2018 +0400;add todos
|
||||
Romanov Anton;Wed Jan 31 15:03:39 2018 +0000;Merge branch 'master' into 'develop'
|
||||
romanov73;Wed Jan 31 18:53:27 2018 +0400;fix round
|
||||
romanov73;Wed Jan 31 18:43:33 2018 +0400;fix round
|
||||
romanov73;Wed Jan 31 18:38:33 2018 +0400;add balance by employees
|
||||
romanov73;Wed Jan 31 18:30:18 2018 +0400;add balance by employees
|
||||
Romanov Anton;Wed Jan 31 13:02:52 2018 +0000;Merge branch 'develop' into 'master'
|
||||
Romanov Anton;Wed Jan 31 12:58:00 2018 +0000;Merge branch '4-' into 'develop'
|
||||
romanov73;Wed Jan 31 16:47:51 2018 +0400;add balance by employees
|
||||
romanov73;Wed Jan 31 00:47:39 2018 +0400;add calculation employee loads
|
||||
romanov73;Wed Jan 31 00:47:25 2018 +0400;add calculation employee loads
|
||||
romanov73;Wed Jan 31 00:47:08 2018 +0400;add calculation employee loads
|
||||
romanov73;Wed Jan 31 00:46:53 2018 +0400;add calculation employee loads
|
||||
romanov73;Wed Jan 31 00:45:59 2018 +0400;add interface for edit additional fields
|
||||
romanov73;Wed Jan 31 00:45:04 2018 +0400;add comparable for using in tree map
|
||||
romanov73;Wed Jan 31 00:44:33 2018 +0400;add fields for production program
|
||||
romanov73;Wed Jan 31 00:43:56 2018 +0400;move interface to base entity
|
||||
romanov73;Wed Jan 31 00:43:21 2018 +0400;add database fields for production program
|
||||
romanov73;Wed Jan 31 00:42:55 2018 +0400;add dynamic columns
|
||||
romanov73;Tue Jan 30 15:11:12 2018 +0400;add work type for workload
|
||||
romanov73;Tue Jan 30 14:55:56 2018 +0400;add comments
|
||||
romanov73;Tue Jan 30 03:00:28 2018 +0400;show by production program
|
||||
romanov73;Tue Jan 30 00:07:31 2018 +0400;add employee available capacity
|
||||
romanov73;Mon Jan 29 23:42:06 2018 +0400;fix units hierarchy bypass
|
||||
romanov73;Mon Jan 29 23:28:28 2018 +0400;divide logic
|
||||
romanov73;Mon Jan 29 22:59:45 2018 +0400;add employee experience table
|
||||
romanov73;Mon Jan 29 22:15:29 2018 +0400;fix calc areas
|
||||
romanov73;Mon Jan 29 21:57:23 2018 +0400;fix calc employee experience
|
||||
romanov73;Mon Jan 29 21:12:52 2018 +0400;calc employee experience by units
|
||||
romanov73;Mon Jan 29 14:01:36 2018 +0400;add employee experience
|
||||
Romanov Anton;Mon Jan 29 06:04:01 2018 +0000;Update README.md
|
||||
Romanov Anton;Sun Jan 28 16:16:56 2018 +0000;Merge branch 'develop' into 'master'
|
||||
Romanov Anton;Sun Jan 28 16:06:14 2018 +0000;Merge branch '3-' into 'develop'
|
||||
romanov73;Sun Jan 28 19:53:21 2018 +0400;remove product work types edit
|
||||
romanov73;Sun Jan 28 19:41:31 2018 +0400;fix months select
|
||||
romanov73;Sun Jan 28 19:06:10 2018 +0400;fix year select
|
||||
romanov73;Sun Jan 28 04:28:21 2018 +0400;fix program edit
|
||||
romanov73;Sun Jan 28 04:27:55 2018 +0400;add product backend
|
||||
romanov73;Sun Jan 28 04:26:53 2018 +0400;add table and fields
|
||||
Romanov Anton;Sat Jan 27 08:11:15 2018 +0000;Merge branch '9-' into 'develop'
|
||||
romanov73;Sat Jan 27 15:28:22 2018 +0400;sort menu items, change logo
|
||||
Romanov Anton;Sat Jan 27 05:55:52 2018 +0000;Update README.md
|
||||
romanov73;Sat Jan 20 00:12:44 2018 +0400;fix versions select
|
||||
Romanov Anton;Fri Jan 19 04:27:48 2018 +0000;Merge branch 'tool-square-catalog' into 'master'
|
||||
Aleksey Filippov;Fri Jan 19 01:11:50 2018 +0400;add tool square catalog migration
|
||||
Aleksey Filippov;Thu Jan 18 02:20:48 2018 +0400;add tool square catalog
|
||||
romanov73;Thu Jan 11 21:22:14 2018 +0400;fix rest path
|
||||
romanov73;Tue Jan 9 03:44:10 2018 +0400;fixes
|
||||
romanov73;Tue Jan 9 00:09:27 2018 +0400;fix unit name
|
||||
romanov73;Tue Jan 9 00:07:47 2018 +0400;fix year
|
||||
romanov73;Mon Jan 8 19:16:28 2018 +0400;add short view
|
||||
romanov73;Mon Jan 8 11:55:30 2018 +0400;fix
|
||||
romanov73;Mon Jan 8 01:24:37 2018 +0400;fill work type by tool name
|
||||
romanov73;Mon Jan 8 00:23:00 2018 +0400;ui fixes
|
||||
romanov73;Sun Jan 7 23:54:40 2018 +0400;fix units hierarchy, add unit type, calc areas balance
|
||||
romanov73;Sat Jan 6 22:51:11 2018 +0400;remove versions
|
||||
romanov73;Sat Jan 6 22:06:03 2018 +0400;fix edit program
|
||||
romanov73;Sat Jan 6 21:40:11 2018 +0400;add title image
|
||||
romanov73;Sat Jan 6 18:04:27 2018 +0400;add production program input
|
||||
romanov73;Wed Jan 3 21:05:48 2018 +0400;change wizard to tabs
|
||||
romanov73;Wed Jan 3 01:03:05 2018 +0400;add wizard
|
||||
romanov73;Sat Dec 30 02:02:42 2017 +0400;fix tools loading
|
||||
romanov73;Sat Dec 30 01:46:01 2017 +0400;fix tools loading
|
||||
romanov73;Sat Dec 30 01:32:33 2017 +0400;fix tools loading
|
||||
romanov73;Sat Dec 30 01:28:24 2017 +0400;fix tools loading
|
||||
romanov73;Sat Dec 30 00:54:26 2017 +0400;fix employee loading
|
||||
romanov73;Fri Dec 29 19:04:14 2017 +0400;fix unit select
|
||||
romanov73;Fri Dec 29 17:19:10 2017 +0400;fix unit select
|
||||
romanov73;Fri Dec 29 01:40:38 2017 +0400;add cache
|
||||
romanov73;Fri Dec 29 01:27:18 2017 +0400;add cache
|
||||
romanov73;Fri Dec 29 01:14:09 2017 +0400;add cache
|
||||
romanov73;Fri Dec 29 01:12:17 2017 +0400;change color
|
||||
romanov73;Fri Dec 29 00:50:57 2017 +0400;add ci
|
||||
romanov73;Fri Dec 29 00:48:10 2017 +0400;add ci
|
||||
romanov73;Fri Dec 29 00:34:36 2017 +0400;add ci
|
||||
romanov73;Fri Dec 29 00:31:22 2017 +0400;add ci
|
||||
romanov73;Fri Dec 29 00:18:51 2017 +0400;add ci
|
||||
romanov73;Fri Dec 29 00:16:44 2017 +0400;add ci
|
||||
romanov73;Fri Dec 29 00:13:33 2017 +0400;add ci
|
||||
romanov73;Fri Dec 29 00:09:51 2017 +0400;fix menus
|
||||
Romanov Anton;Thu Dec 28 19:47:02 2017 +0000;Merge branch 'balance-example' into 'master'
|
||||
romanov73;Thu Dec 28 23:44:52 2017 +0400;add other dictionaries
|
||||
romanov73;Sat Dec 23 10:40:41 2017 +0400;add tool and work types
|
||||
romanov73;Sat Dec 23 09:12:38 2017 +0400;fix menu item
|
||||
romanov73;Sat Dec 23 09:12:23 2017 +0400;fix employee load
|
||||
romanov73;Sat Dec 23 08:51:18 2017 +0400;add employee category
|
||||
romanov73;Sat Dec 23 08:22:10 2017 +0400;add categories dictionary
|
||||
romanov73;Fri Dec 22 12:38:08 2017 +0400;add menu item
|
||||
romanov73;Fri Dec 22 12:31:26 2017 +0400;add employee category
|
||||
romanov73;Fri Dec 22 09:55:31 2017 +0400;fix calendar
|
||||
romanov73;Fri Dec 22 09:33:56 2017 +0400;fix date
|
||||
romanov73;Thu Dec 21 16:38:20 2017 +0400;fix for context path change
|
||||
romanov73;Thu Dec 21 13:58:02 2017 +0400;fluid panel
|
||||
romanov73;Wed Dec 20 20:50:17 2017 +0400;fix context path
|
||||
romanov73;Wed Dec 20 17:12:19 2017 +0400;fix displaying position
|
||||
romanov73;Wed Dec 20 17:10:14 2017 +0400;load employees from file
|
||||
romanov73;Wed Dec 20 15:09:54 2017 +0400;fix tool loading
|
||||
romanov73;Mon Dec 18 18:18:04 2017 +0400;add resource versions
|
||||
romanov73;Mon Dec 18 17:36:50 2017 +0400;add resource
|
||||
romanov73;Mon Dec 18 17:07:47 2017 +0400;add swagger
|
||||
romanov73;Sat Dec 16 09:42:46 2017 +0400;add new version
|
||||
romanov73;Fri Dec 15 19:50:46 2017 +0400;fix title
|
||||
romanov73;Fri Dec 15 19:24:13 2017 +0400;fix title
|
||||
romanov73;Fri Dec 15 11:57:18 2017 +0400;add menu items
|
||||
romanov73;Fri Dec 15 11:52:01 2017 +0400;add positions dictionary
|
||||
romanov73;Fri Dec 15 11:29:02 2017 +0400;fix select for employee
|
||||
romanov73;Fri Dec 15 10:35:12 2017 +0400;add employee backing
|
||||
romanov73;Fri Dec 15 09:36:33 2017 +0400;fix 500 error page
|
||||
romanov73;Thu Dec 14 22:26:45 2017 +0400;fix mapping
|
||||
romanov73;Thu Dec 14 22:49:44 2017 +0400;add employee
|
||||
romanov73;Thu Dec 14 22:37:30 2017 +0400;add employee
|
||||
romanov73;Thu Dec 14 14:12:03 2017 +0400;fix crud service
|
||||
romanov73;Thu Dec 14 12:51:59 2017 +0400;fix table style
|
||||
romanov73;Thu Dec 14 12:22:48 2017 +0400;remove border for grid
|
||||
romanov73;Thu Dec 14 12:20:14 2017 +0400;add version_id for units
|
||||
romanov73;Thu Dec 14 11:49:53 2017 +0400;fix font size
|
||||
romanov73;Thu Dec 14 11:08:46 2017 +0400;add unit fields
|
||||
romanov73;Thu Dec 14 10:23:01 2017 +0400;KISS units hierarchy
|
||||
romanov73;Wed Dec 13 19:04:05 2017 +0400;refresh page
|
||||
romanov73;Wed Dec 13 17:43:54 2017 +0400;add version
|
||||
romanov73;Wed Dec 13 01:46:05 2017 +0400;body width
|
||||
romanov73;Wed Dec 13 01:45:17 2017 +0400;add menu item
|
||||
romanov73;Tue Dec 12 22:20:07 2017 +0400;fix tree
|
||||
romanov73;Tue Dec 12 20:02:20 2017 +0400;fix login
|
||||
romanov73;Tue Dec 12 19:29:03 2017 +0400;add unit tree
|
||||
romanov73;Tue Dec 12 18:23:38 2017 +0400;add unit dictionary
|
||||
romanov73;Tue Dec 12 16:18:03 2017 +0400;add filter
|
||||
romanov73;Tue Dec 12 14:13:31 2017 +0400;fix menu session
|
||||
romanov73;Tue Dec 12 00:10:47 2017 +0400;remove bootstrap
|
||||
romanov73;Mon Dec 11 21:53:18 2017 +0400;add unit select
|
||||
romanov73;Mon Dec 11 19:49:15 2017 +0400;add menu item and role
|
||||
romanov73;Mon Dec 11 19:37:38 2017 +0400;fix permissions
|
||||
romanov73;Mon Dec 11 17:08:39 2017 +0400;refactor and add tools dictionary backing
|
||||
romanov73;Mon Dec 11 17:08:04 2017 +0400;refactor and add tools dictionary backing
|
||||
romanov73;Mon Dec 11 17:07:27 2017 +0400;refactor and add tools dictionary backing
|
||||
romanov73;Mon Dec 11 14:12:24 2017 +0400;add tools dictionary page
|
||||
romanov73;Sat Dec 9 09:54:45 2017 +0400;add global exception hadler
|
||||
romanov73;Sat Nov 25 13:52:33 2017 +0400;add monitoring
|
||||
romanov73;Sat Nov 25 13:21:18 2017 +0400;fix xls and xlsx
|
||||
romanov73;Sat Nov 25 12:39:45 2017 +0400;upload tools
|
||||
romanov73;Fri Nov 24 22:16:27 2017 +0400;save tools
|
||||
romanov73;Fri Nov 24 13:06:21 2017 +0400;fix message
|
||||
romanov73;Fri Nov 24 12:11:43 2017 +0400;add loading from xlsx
|
||||
romanov73;Fri Nov 17 19:43:35 2017 +0400;add message
|
||||
romanov73;Fri Nov 17 15:11:22 2017 +0400;fix button
|
||||
romanov73;Fri Nov 17 10:15:42 2017 +0400;add registration service
|
||||
romanov73;Thu Nov 16 21:35:04 2017 +0400;add push script
|
||||
romanov73;Thu Nov 16 20:54:49 2017 +0400;fix named query execution
|
||||
romanov73;Sun Nov 12 22:19:03 2017 +0400;sort menu items
|
||||
romanov73;Sat Nov 11 15:09:35 2017 +0400;fix title
|
||||
romanov73;Sat Nov 11 14:54:25 2017 +0400;fix table
|
||||
romanov73;Sat Nov 11 14:35:38 2017 +0400;fix style attribute
|
||||
romanov73;Sat Nov 11 14:08:56 2017 +0400;show user sessions
|
||||
romanov73;Sat Nov 11 13:51:34 2017 +0400;rename service
|
||||
romanov73;Fri Nov 10 22:03:25 2017 +0400;fix update tree
|
||||
romanov73;Fri Nov 10 14:29:25 2017 +0400;edit menu items
|
||||
romanov73;Fri Nov 10 12:17:24 2017 +0400;set bootstrap theme for primefaces
|
||||
romanov73;Thu Nov 9 23:40:40 2017 +0400;fix saving entitites
|
||||
romanov73;Thu Nov 9 23:27:42 2017 +0400;fix saving entitites
|
||||
romanov73;Thu Nov 9 20:51:19 2017 +0400;fix titles
|
||||
romanov73;Thu Nov 9 18:00:45 2017 +0400;fix login page title
|
||||
romanov73;Thu Nov 9 13:36:21 2017 +0400;commit log
|
||||
romanov73;Thu Nov 9 07:31:37 2017 +0400;reverse sort commits
|
||||
romanov73;Thu Nov 9 00:27:48 2017 +0400;sort commits
|
||||
romanov73;Wed Nov 8 23:16:07 2017 +0400;extend commits log
|
||||
romanov73;Wed Nov 8 21:42:16 2017 +0400;fix styles
|
||||
romanov73;Wed Nov 8 19:17:08 2017 +0400;add logout
|
||||
romanov73;Wed Nov 8 19:13:17 2017 +0400;add logout
|
||||
romanov73;Wed Nov 8 20:18:35 2017 +0400;Merge branch 'master' of gitlab.com:romanov73/balance
|
||||
romanov73;Wed Nov 8 20:17:55 2017 +0400;add database diagramm
|
||||
Romanov Anton;Wed Nov 8 04:13:39 2017 +0000;Update README.md
|
||||
Romanov Anton;Wed Nov 8 04:11:33 2017 +0000;Update README.md
|
||||
romanov73;Wed Nov 8 07:48:13 2017 +0400;Merge remote-tracking branch 'origin/master'
|
||||
Romanov Anton;Tue Nov 7 18:05:04 2017 +0000;Update README.md
|
||||
romanov73;Tue Nov 7 22:01:50 2017 +0400;add example of permissions validation
|
||||
romanov73;Tue Nov 7 20:56:31 2017 +0400;rename project
|
||||
romanov73;Wed Oct 11 21:37:49 2017 +0400;reverse sort commits
|
||||
romanov73;Wed Oct 11 21:32:19 2017 +0400;fix read resource as file
|
||||
romanov73;Wed Oct 11 20:23:10 2017 +0400;Merge remote-tracking branch 'origin/master'
|
||||
romanov73;Wed Oct 11 20:22:52 2017 +0400;add commits log
|
||||
Romanov Anton;Tue Oct 10 20:47:29 2017 +0000;Update README.md
|
||||
romanov73;Fri Oct 6 01:55:57 2017 +0400;add menu to platform
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user