initial commit with existing sources

master
Vyacheslav N. Boyko 2017-12-09 22:57:41 +03:00
parent 38cfe9dfca
commit 10502673ad
454 changed files with 31188 additions and 19 deletions

41
.gitignore vendored
View File

@ -1,22 +1,25 @@
# Compiled class file
*.class
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
# Log file
*.log
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
# BlueJ files
*.ctxt
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/

132
build.gradle 100644
View File

@ -0,0 +1,132 @@
buildscript {
ext {
springBootVersion = '1.5.10.BUILD-SNAPSHOT'
redisEmbeddedVersion = "0.6"
}
repositories {
mavenCentral()
jcenter()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.10.BUILD-SNAPSHOT")
}
}
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
group = 'ru.bvn13'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
jcenter()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
maven { url 'https://repo.spring.io/libs-release' }
maven { url "https://repo.springsource.org/repo" }
}
bootRun {
systemProperties = System.properties
}
configurations {
compile.exclude module: "spring-boot-starter-tomcat"
all*.exclude module: 'spring-boot-starter-logging'
}
dependencies {
// --- spring boot ---
//compile('org.springframework.boot:spring-boot-starter-cache')
compile('org.springframework.boot:spring-boot-starter-data-jpa:1.5.10.BUILD-SNAPSHOT')
compile('org.springframework.boot:spring-boot-starter-security:1.5.10.BUILD-SNAPSHOT')
compile('org.springframework.session:spring-session:1.3.1.RELEASE')
compile('org.springframework.boot:spring-boot-starter-validation:1.5.10.BUILD-SNAPSHOT')
compile('org.springframework.boot:spring-boot-starter-web:1.5.10.BUILD-SNAPSHOT')
compile('org.springframework.boot:spring-boot-starter-jetty:1.5.10.BUILD-SNAPSHOT')
compile('org.springframework.boot:spring-boot-starter-thymeleaf:1.5.10.BUILD-SNAPSHOT')
compile('com.domingosuarez.boot:spring-boot-starter-jade4j:0.3.2')
compile "org.springframework.boot:spring-boot-starter-data-redis:1.5.10.BUILD-SNAPSHOT",
"com.github.kstyrc:embedded-redis:$redisEmbeddedVersion"
compile(group: 'org.springframework.boot', name: 'spring-boot-starter') {
exclude(module: 'spring-boot-starter-logging')
}
compile('org.springframework.boot:spring-boot-starter-log4j:1.3.7.RELEASE')
// --- spring ---
compile('org.springframework:spring-context:4.3.13.RELEASE')
compile('org.springframework:spring-webmvc:4.3.13.RELEASE')
compile('org.springframework.security:spring-security-config:4.2.3.RELEASE')
compile('org.springframework.security:spring-security-web:4.2.3.RELEASE')
// --- spring data ---
compile('org.springframework.data:spring-data-jpa:1.11.9.RELEASE')
// --- persistence ---
compile('com.zaxxer:HikariCP:2.5.1')
compile('org.springframework:spring-orm:4.3.13.RELEASE')
compile('org.hibernate:hibernate-entitymanager:5.0.12.Final')
compile('javax.el:javax.el-api:+')
compile('org.hsqldb:hsqldb:2.3.5')
// --- view ---
compile('de.neuland-bfi:spring-jade4j:1.2.7')
//markdown processor
compile 'com.vladsch.flexmark:flexmark-all:0.28.8'
// --- cache ---
compile('org.springframework.data:spring-data-redis:1.8.9.RELEASE')
compile('redis.clients:jedis:2.9.0')
// --- PostgreSQL ---
runtime('org.postgresql:postgresql')
// --- Validation ---
compile 'org.hibernate:hibernate-validator:6.0.5.Final'
//compile 'org.hibernate.validator:hibernate-validator:6.0.5.Final'
//compile 'org.hibernate.validator:hibernate-validator-annotation-processor:6.0.5.Final'
compile 'javax.validation:validation-api:2.0.0.Final'
// Logging
compile('ch.qos.logback:logback-classic')
compile('org.slf4j:slf4j-api')
compile('org.apache.commons:commons-lang3')
// @Inject
compile('javax.inject:javax.inject:+')
// JSON
compile('com.fasterxml.jackson.core:jackson-databind')
compile('com.fasterxml.jackson.core:jackson-annotations')
// Utilities
compile('com.google.guava:guava:23.0')
compile('org.modelmapper:modelmapper:1.1.1')
compile('org.projectlombok:lombok:1.16.18')
// static resources, ref. http://www.webjars.org/
compile('org.webjars:jquery:3.2.1')
compile('org.webjars:bootstrap:3.3.7-1')
compile('org.webjars:font-awesome:4.7.0')
compile('org.webjars:ace:1.2.8')
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.springframework.security:spring-security-test')
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored 100644

Binary file not shown.

View File

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

172
gradlew vendored 100644
View File

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## 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
# Escape application args
save ( ) {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# 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" "$@"

84
gradlew.bat vendored 100644
View 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

View File

@ -0,0 +1,54 @@
package ru.bvn13.voidforum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import redis.embedded.Redis;
import redis.embedded.RedisServer;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import static java.util.stream.Collectors.joining;
/**
* @author bvn13 <mail4bvn@gmail.com>
*/
@SpringBootApplication
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
@Autowired
@Qualifier("RedisServer")
private Redis redisServer;
@PostConstruct
public void start() {
logger.info("starting redis...");
if (!redisServer.isActive()) {
redisServer.start();
}
if (redisServer.isActive()) {
logger.info("redis listen ports: {}", redisServer.ports().stream().map(Object::toString).collect(joining(",")));
}
}
@PreDestroy
public void stop() {
logger.info("shutting down redis...");
redisServer.stop();
logger.info("bye!");
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,21 @@
package ru.bvn13.voidforum;
/**
* @author: bvn13 <mail4bvn@gmail.com>
*/
public final class Constants {
public static final String ENV_PRODUCTION = "production";
public static final String ENV_DEVELOPMENT = "development";
public static final String DEFAULT_ADMIN_NICKNAME = "admin";
public static final String DEFAULT_ADMIN_EMAIL = "admin@admin.com";
public static final String DEFAULT_ADMIN_PASSWORD = "admin";
public static final String ABOUT_PAGE_PERMALINK = "about";
}

View File

@ -0,0 +1,104 @@
package ru.bvn13.voidforum;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.hibernate.SessionFactory;
import org.hibernate.jpa.HibernateEntityManagerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaSessionFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;
/**
* @author bvn13 <mail4bvn@gmail.com>
*/
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackageClasses = Application.class)
class JpaConfig implements TransactionManagementConfigurer {
@Value("${spring.dataSource.driverClassName}")
private String driver;
@Value("${spring.dataSource.url}")
private String url;
@Value("${spring.dataSource.username}")
private String username;
@Value("${spring.dataSource.password}")
private String password;
@Value("${spring.hibernate.dialect}")
private String dialect;
@Value("${spring.hibernate.hbm2ddl.auto}")
private String hbm2ddlAuto;
@Value("${spring.hibernate.show_sql}")
private Boolean showSql;
@Bean
public DataSource configureDataSource() {
HikariConfig config = new HikariConfig();
config.setDriverClassName(driver);
config.setJdbcUrl(url);
config.setUsername(username);
config.setPassword(password);
config.addDataSourceProperty("useUnicode", "true");
config.addDataSourceProperty("characterEncoding", "utf8");
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
config.addDataSourceProperty("useServerPrepStmts", "true");
return new HikariDataSource(config);
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(configureDataSource());
entityManagerFactoryBean.setPackagesToScan("ru.bvn13.voidforum");
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Properties jpaProperties = new Properties();
jpaProperties.put(org.hibernate.cfg.Environment.DIALECT, dialect);
jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, hbm2ddlAuto);
jpaProperties.put(org.hibernate.cfg.Environment.SHOW_SQL, showSql);
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
@Bean(name = "transactionManager")
public PlatformTransactionManager annotationDrivenTransactionManager() {
return new JpaTransactionManager();
}
/*
@Autowired
private EntityManagerFactory entityManagerFactory;
@Bean
public SessionFactory getSessionFactory() {
if (entityManagerFactory.unwrap(SessionFactory.class) == null) {
throw new NullPointerException("factory is not a hibernate factory");
}
return entityManagerFactory.unwrap(SessionFactory.class);
}*/
}

View File

@ -0,0 +1,59 @@
package ru.bvn13.voidforum;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.embedded.Redis;
import redis.embedded.RedisServer;
import redis.embedded.exceptions.EmbeddedRedisException;
import java.util.List;
@Configuration
public class RedisConfig {
public static class RedisDummy implements Redis {
@Override
public boolean isActive() {
return false;
}
@Override
public void start() throws EmbeddedRedisException {
}
@Override
public void stop() throws EmbeddedRedisException {
}
@Override
public List<Integer> ports() {
return null;
}
}
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.embedded}")
private Boolean useEmbeddedRedis;
@Bean(name = "RedisServer")
public Redis redisServer() {
if (!this.useEmbeddedRedis) {
return new RedisDummy();
}
RedisServer.builder().reset();
return RedisServer.builder()
.port(this.port)
.build();
}
}

View File

@ -0,0 +1,67 @@
package ru.bvn13.voidforum;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
import ru.bvn13.voidforum.services.PrivilegeService;
import ru.bvn13.voidforum.services.UserService;
/**
* @author bvn13 <mail4bvn@gmail.com>
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public UserService userService() {
return new UserService();
}
@Bean
public TokenBasedRememberMeServices rememberMeServices() {
return new TokenBasedRememberMeServices("remember-me-key", userService());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new StandardPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.eraseCredentials(true)
.userDetailsService(userService())
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasAnyAuthority(PrivilegeService.PRIVILEGE_ADMIN, PrivilegeService.PRIVILEGE_OWNER)
.antMatchers("/account/**").hasAnyAuthority(PrivilegeService.PRIVILEGE_WRITE, PrivilegeService.PRIVILEGE_ADMIN, PrivilegeService.PRIVILEGE_OWNER)
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/signin")
.permitAll()
.failureUrl("/signin?error=1")
.loginProcessingUrl("/authenticate")
.and()
.logout()
.logoutUrl("/logout")
.permitAll()
.logoutSuccessUrl("/signin?logout")
.and()
.rememberMe()
.rememberMeServices(rememberMeServices())
.key("remember-me-key");
}
}

View File

@ -0,0 +1,66 @@
package ru.bvn13.voidforum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import ru.bvn13.voidforum.support.web.ViewHelperVF;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static ru.bvn13.voidforum.Constants.*;
/**
* @author bvn13 <mail4bvn@gmail.com>.
*/
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private ViewHelperVF viewHelper;
@Autowired
private Environment env;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(viewObjectAddingInterceptor());
super.addInterceptors(registry);
}
@PostConstruct
public void registerJadeViewHelpers(){
viewHelper.setApplicationEnv(this.getApplicationEnv());
}
@Bean
public HandlerInterceptor viewObjectAddingInterceptor() {
return new HandlerInterceptorAdapter() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
viewHelper.setStartTime(System.currentTimeMillis());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView view) {
CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (token != null) {
view.addObject(token.getParameterName(), token);
}
}
};
}
public String getApplicationEnv(){
return this.env.acceptsProfiles(ENV_PRODUCTION) ? ENV_PRODUCTION : ENV_DEVELOPMENT;
}
}

View File

@ -0,0 +1,87 @@
package ru.bvn13.voidforum.controllers;
import ru.bvn13.voidforum.Constants;
import ru.bvn13.voidforum.error.NotFoundException;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.services.AppSetting;
import ru.bvn13.voidforum.services.PostService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import ru.bvn13.voidforum.services.PrivilegeService;
import ru.bvn13.voidforum.services.UserService;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
@Autowired
private PostService postService;
@Autowired
private UserService userService;
@Autowired
private AppSetting appSetting;
@RequestMapping(value = "", method = GET)
public String index(@RequestParam(defaultValue = "1") int page, Model model, HttpServletRequest request) {
//request.isUserInRole("READ_PRIVILEGE");
page = page < 1 ? 0 : page - 1;
Page<Post> posts = null;
if (userService.currentUserHasPrivilege(PrivilegeService.PRIVILEGE_WRITE)) {
if (userService.currentUserHasPrivilege(PrivilegeService.PRIVILEGE_ADMIN)) {
posts = postService.getAllPublishedPostsByPage(page, appSetting.getPageSize());
} else {
posts = postService.getAllPublishedNotDeletedPostsByPage(page, appSetting.getPageSize());
}
} else {
posts = postService.getAllPublishedNotCensoredNotDeletedPostsByPage(page, appSetting.getPageSize());
}
model.addAttribute("totalPages", posts.getTotalPages());
model.addAttribute("posts", posts);
model.addAttribute("page", page + 1);
return "home/index";
}
@RequestMapping(value = "about", method = GET)
public String about(Model model) {
/*Post post = null;
try {
post = postService.getPublishedPostByPermalink(Constants.ABOUT_PAGE_PERMALINK);
} catch (NotFoundException nfe) {
logger.debug("Get post with permalink " + Constants.ABOUT_PAGE_PERMALINK);
post = postService.createAboutPage();
}
if (post == null) {
throw new NotFoundException("Post with permalink " + Constants.ABOUT_PAGE_PERMALINK + " is not found");
}
*/
Post post = null;
try {
post = postService.getPublishedPostByPermalink(Constants.ABOUT_PAGE_PERMALINK);
} catch (NotFoundException nfe) {
logger.debug("Get post with permalink " + Constants.ABOUT_PAGE_PERMALINK);
throw new NotFoundException("Post with permalink " + Constants.ABOUT_PAGE_PERMALINK + " is not found");
}
model.addAttribute("about", post);
return "home/about";
}
}

View File

@ -0,0 +1,84 @@
package ru.bvn13.voidforum.controllers;
import ru.bvn13.voidforum.forms.CommentForm;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.SeoPostData;
import ru.bvn13.voidforum.services.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import static org.springframework.web.bind.annotation.RequestMethod.*;
/**
* bvn13 <mail4bvn@gmail.com>
*/
@Controller
@RequestMapping("posts")
public class PostController {
Logger logger = LoggerFactory.getLogger(PostController.class);
@Autowired
private PostService postService;
@Autowired
private VisitService visitService;
@Autowired
private LikeService likeService;
@Autowired
private CommentService commentService;
@Autowired
private RequestProcessorService requestProcessorService;
@RequestMapping(value = "archive", method = GET)
public String archive(Model model){
model.addAttribute("posts", postService.getArchivePosts());
return "posts/archive";
}
@RequestMapping(value = "{permalink}", method = GET)
public String show(@PathVariable String permalink, Model model, HttpServletRequest request){
Post post = this.postService.findPostByPermalink(permalink);
logger.debug(String.format("ACCESS %s from IP: %s", permalink, this.requestProcessorService.getRealIp(request)));
this.visitService.saveVisit(post, this.requestProcessorService.getRealIp(request), this.requestProcessorService.getUserAgent(request));
post.setVisitsCount(this.visitService.getUniqueVisitsCount(post));
post.setSympathyCount(this.likeService.getTotalLikesByPost(post));
SeoPostData seoData = null;
if (post.getSeoData() == null) {
seoData = new SeoPostData();
seoData.setPost(post);
} else {
seoData = post.getSeoData();
}
model.addAttribute("post", post);
model.addAttribute("tags", this.postService.getPostTags(post));
model.addAttribute("seoKeywords", this.postService.getSeoKeywordsAsString(post));
model.addAttribute("seoDescription", post.getSeoDescription());
model.addAttribute("seoData", seoData);
model.addAttribute("comments", commentService.getCommentsForPost(post));
model.addAttribute("commentForm", new CommentForm());
model.addAttribute("commentFormats", commentService.getAvailableCommentFormats());
return "posts/show";
}
}

View File

@ -0,0 +1,61 @@
package ru.bvn13.voidforum.controllers;
import ru.bvn13.voidforum.models.StoredFile;
import ru.bvn13.voidforum.services.FileStorageService;
import ru.bvn13.voidforum.services.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.FileNotFoundException;
import java.io.IOException;
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM;
import static org.springframework.util.MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE;
@Controller
@RequestMapping("/files")
public class StoredFileController {
@Autowired
private UserService userService;
@Autowired
private FileStorageService storageService;
@GetMapping(value = "/{fileName:.+}", produces = APPLICATION_OCTET_STREAM_VALUE)
@ExceptionHandler(value = FileNotFoundException.class)
public @ResponseBody
HttpEntity<byte[]> getFileById(@PathVariable String fileName, final HttpServletResponse response) throws IOException {
StoredFile file = this.storageService.getFileByName(fileName);
if (file == null) {
response.sendError(404, String.format("File %s not found", fileName));
return null;
}
byte[] content;
try {
content = this.storageService.getFileContent(file.getPath());
} catch (IOException e) {
e.printStackTrace();
if (this.userService.isCurrentUserAdmin()) {
response.sendError(404, String.format("File %s (%s) not found", file.getName(), file.getPath()));
} else {
response.sendError(404, String.format("File %s not found", file.getName()));
}
return null;
}
HttpHeaders header = new HttpHeaders();
header.setContentType(APPLICATION_OCTET_STREAM);
header.set("Content-Disposition", "inline; filename=" + file.getName());
header.setContentLength(file.getSize());
return new HttpEntity<byte[]>(content, header);
}
}

View File

@ -0,0 +1,69 @@
package ru.bvn13.voidforum.controllers;
import ru.bvn13.voidforum.forms.LikeForm;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.services.*;
import ru.bvn13.voidforum.support.web.ViewHelperVF;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
@Controller
@RequestMapping(value = "/sympathy")
public class SympathyController {
@Autowired
private AppSetting appSetting;
@Autowired
private PostService postService;
@Autowired
private LikeService likeService;
@Autowired
private UserService userService;
@Autowired
private RequestProcessorService requestProcessorService;
@Data
public static class SympathyRequestData {
@NotNull
private String postId;
}
@PostMapping(value = "/like")
public @ResponseBody
LikeForm likeIt(@RequestBody SympathyRequestData data, HttpServletRequest request) {
Post post = this.postService.findPostByPermalink(data.getPostId());
this.likeService.likePost(post, this.requestProcessorService.getRealIp(request));
ViewHelperVF viewHelper = new ViewHelperVF(this.appSetting);
LikeForm result = new LikeForm();
result.setSympathy(viewHelper.formatNumberByThousands(this.likeService.getTotalLikesByPost(post)));
return result;
}
@PostMapping(value = "/dislike")
public @ResponseBody
LikeForm dislikeIt(@RequestBody SympathyRequestData data, HttpServletRequest request) {
Post post = this.postService.findPostByPermalink(data.getPostId());
this.likeService.dislikePost(post, this.requestProcessorService.getRealIp(request));
ViewHelperVF viewHelper = new ViewHelperVF(this.appSetting);
LikeForm result = new LikeForm();
result.setSympathy(viewHelper.formatNumberByThousands(this.likeService.getTotalLikesByPost(post)));
return result;
}
}

View File

@ -0,0 +1,58 @@
package ru.bvn13.voidforum.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import ru.bvn13.voidforum.error.NotFoundException;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.Tag;
import ru.bvn13.voidforum.services.AppSetting;
import ru.bvn13.voidforum.services.PostService;
import ru.bvn13.voidforum.services.TagService;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
/**
* bvn13 <mail4bvn@gmail.com>.
*/
@Controller
@RequestMapping("tags")
public class TagController {
@Autowired
private TagService tagService;
@Autowired
private PostService postService;
@Autowired
private AppSetting appSetting;
@RequestMapping(value = "", method = GET)
public String index(Model model){
model.addAttribute("tags", postService.countPostsByTags());
return "tags/index";
}
@RequestMapping(value = "{tagName}", method = GET)
public String showTag(@PathVariable String tagName, @RequestParam(defaultValue = "1") int page, Model model) {
Tag tag = tagService.getTag(tagName);
if (tag == null) {
throw new NotFoundException("Tag " + tagName + " is not found.");
}
page = page < 1 ? 0 : page - 1;
Page<Post> posts = postService.findPostsByTag(tagName, page, appSetting.getPageSize());
model.addAttribute("tag", tag);
model.addAttribute("posts", posts);
model.addAttribute("page", page + 1);
model.addAttribute("totalPages", posts.getTotalPages());
return "tags/show";
}
}

View File

@ -0,0 +1,22 @@
package ru.bvn13.voidforum.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* Created by bvn13 on 09.12.2017.
*/
@Controller
@RequestMapping("tests")
public class TestController {
@GetMapping(value = "/1")
public String test1(Model model) {
return "tests/1";
}
}

View File

@ -0,0 +1,90 @@
package ru.bvn13.voidforum.controllers;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import ru.bvn13.voidforum.error.EmailExistsException;
import ru.bvn13.voidforum.error.NicknameExistsException;
import ru.bvn13.voidforum.forms.RegistrationForm;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.services.UserService;
import ru.bvn13.voidforum.utils.DTOUtil;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.security.Principal;
/**
* bvn13 <mail4bvn@gmail.com>
*/
@Controller(value = "/")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "signin", method = RequestMethod.GET)
public String signin(Principal principal, RedirectAttributes ra) {
return principal == null ? "users/signin" : "redirect:/";
}
@GetMapping(value = "register")
public String registrationForm(Model model) {
model.addAttribute("form", new RegistrationForm());
return "users/register";
}
@PostMapping(value = "register")
public String register(@Valid RegistrationForm registrationForm, Errors errors, Model model, RedirectAttributes ra) {
if (!registrationForm.getPassword().equals(registrationForm.getPasswordCheck())) {
ra.addFlashAttribute("error", "Verify your password!");
return "redirect:/register";
}
if (registrationForm.getUsername().isEmpty()
|| registrationForm.getNickname().isEmpty()
|| registrationForm.getPassword().isEmpty()
|| registrationForm.getPasswordCheck().isEmpty()) {
ra.addFlashAttribute("error", "Not all necessary fields are specified");
return "redirect:/register";
}
User user = new User();
user.setEmail(registrationForm.getUsername());
user.setNickname(registrationForm.getNickname());
user.setPassword(registrationForm.getPassword());
//DTOUtil.mapTo(registrationForm, user);
try {
userService.registerNewUserAccount(user);
} catch (EmailExistsException e) {
e.printStackTrace();
ra.addFlashAttribute("error", "There is an account with specified email and nickname");
return "redirect:/register";
} catch (NicknameExistsException e) {
e.printStackTrace();
ra.addFlashAttribute("error", "There is an account with specified email and nickname");
return "redirect:/register";
}
return "redirect:/signin";
}
}

View File

@ -0,0 +1,23 @@
package ru.bvn13.voidforum.controllers.account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* Created by bvn13 on 08.12.2017.
*/
@Controller
@RequestMapping(value = "/account")
public class AccountController {
@GetMapping(value = "")
public String getAccountIndex(Model model) {
return "account/home/index";
}
}

View File

@ -0,0 +1,23 @@
package ru.bvn13.voidforum.controllers.account;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
/**
* Created by bvn13 on 08.12.2017.
*/
@Controller("accountCommentController")
@RequestMapping("/account/comments")
public class CommentController {
@RequestMapping(value = "", method = POST)
public String addComment(Model model) {
return "";
}
}

View File

@ -0,0 +1,193 @@
package ru.bvn13.voidforum.controllers.account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.Assert;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import ru.bvn13.voidforum.forms.PostForm;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.models.support.*;
import ru.bvn13.voidforum.repositories.PostRepository;
import ru.bvn13.voidforum.repositories.UserRepository;
import ru.bvn13.voidforum.services.PostService;
import ru.bvn13.voidforum.services.PrivilegeService;
import ru.bvn13.voidforum.services.UserService;
import ru.bvn13.voidforum.utils.DTOUtil;
import javax.validation.Valid;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
/**
* Created by bvn13 on 07.12.2017.
*/
@Controller("accountPostController")
@RequestMapping(value = "/account/posts")
public class PostController {
@Autowired
private PostRepository postRepository;
@Autowired
private PostService postService;
@Autowired
private UserRepository userRepository;
@Autowired
private UserService userService;
private static final int PAGE_SIZE = 20;
@GetMapping(name = "")
public String getOwnedPosts(@RequestParam(defaultValue = "0") int page, Model model) {
Assert.notNull(userService.currentUser(), "");
Page<Post> posts = postRepository.findAllByUser(userService.currentUser(), new PageRequest(page, PAGE_SIZE, Sort.Direction.DESC, "id"));
model.addAttribute("totalPages", posts.getTotalPages());
model.addAttribute("page", page);
model.addAttribute("posts", posts);
return "account/posts/index";
}
private String makeFormPostCreation(Model model) {
PostForm postForm = DTOUtil.map(new Post(), PostForm.class);
postForm.init();
return this.makeFormPostCreation(model, postForm);
}
private String makeFormPostCreation(Model model, PostForm postForm) {
User user = userService.currentUser();
List<PostFormat> availableFormats = userService.getAvailablePostFormats(user);
model.addAttribute("postForm", postForm);
model.addAttribute("postFormats", availableFormats);
model.addAttribute("postStatus", PostStatus.values());
model.addAttribute("seoOgLocales", OgLocale.values());
model.addAttribute("seoOgTypes", OgType.values());
return "account/posts/new";
}
private String makeFormPostEdition(Long postId, Model model) {
return this.makeFormPostEdition(postId, model, null);
}
private String makeFormPostEdition(Long postId, Model model, PostForm postForm) {
Post post = postRepository.findOne(postId);
if (postForm == null) {
postForm = DTOUtil.map(post, PostForm.class);
}
postForm.init();
DTOUtil.mapTo(post, postForm);
postForm.initFromPost(post, postService.getTagNames(post.getTags()));
User user = userService.currentUser();
List<PostFormat> availableFormats = userService.getAvailablePostFormats(user);
model.addAttribute("post", post);
model.addAttribute("postForm", postForm);
model.addAttribute("postFormats", availableFormats);
model.addAttribute("postStatus", PostStatus.values());
model.addAttribute("seoOgLocales", OgLocale.values());
model.addAttribute("seoOgTypes", OgType.values());
return "account/posts/edit";
}
private void checkAccess(Long postId) {
if (!postService.getPost(postId).getUser().getId().equals(userService.currentUser().getId())) {
throw new AccessDeniedException("You are not allowed to be here");
}
}
@RequestMapping(value = "{postId:[0-9]+}/edit")
public String editPost(@PathVariable Long postId, Model model) {
this.checkAccess(postId);
return this.makeFormPostEdition(postId, model);
}
@RequestMapping(value = "new")
public String newPost(Model model){
return this.makeFormPostCreation(model);
}
@RequestMapping(value = "{postId:[0-9]+}/delete", method = {DELETE, POST})
public String deletePost(@PathVariable Long postId){
this.checkAccess(postId);
postService.deletePost(postRepository.findOne(postId));
return "redirect:/account/posts";
}
@RequestMapping(value = "", method = POST)
public String create(Principal principal, @Valid PostForm postForm, Errors errors, Model model){
if (errors.hasErrors()) {
Map<String, WebError> webErrors = new HashMap<>();
errors.getAllErrors().forEach(e -> {
String field = ((FieldError)e).getField();
webErrors.put(field, new WebError(field, e.getDefaultMessage()));
});
model.addAttribute("errors", webErrors);
return this.makeFormPostCreation(model, postForm);
} else {
Post post = DTOUtil.map(postForm, Post.class);
post.setUser(userRepository.findByEmail(principal.getName()));
post.setTags(postService.parseTagNames(postForm.getPostTags()));
postForm.fillOgFieldsInPost(post);
postService.createPost(post);
return "redirect:/account/posts";
}
}
@RequestMapping(value = "{postId:[0-9]+}", method = {PUT, POST})
public String update(@PathVariable Long postId, @Valid PostForm postForm, Errors errors, Model model){
this.checkAccess(postId);
if (errors.hasErrors()){
Map<String, WebError> webErrors = new HashMap<>();
errors.getAllErrors().forEach(e -> {
String field = ((FieldError)e).getField();
webErrors.put(field, new WebError(field, e.getDefaultMessage()));
});
model.addAttribute("errors", webErrors);
return this.makeFormPostEdition(postId, model, postForm);
} else {
Post post = postRepository.findOne(postId);
DTOUtil.mapTo(postForm, post);
post.setTags(postService.parseTagNames(postForm.getPostTags()));
postForm.fillOgFieldsInPost(post);
postService.updatePost(post);
return "redirect:/account/posts";
}
}
}

View File

@ -0,0 +1,135 @@
package ru.bvn13.voidforum.controllers.account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.Assert;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import ru.bvn13.voidforum.error.NotFoundException;
import ru.bvn13.voidforum.forms.StoredFileForm;
import ru.bvn13.voidforum.models.StoredFile;
import ru.bvn13.voidforum.repositories.StoredFileRepository;
import ru.bvn13.voidforum.services.FileStorageService;
import ru.bvn13.voidforum.services.UserService;
import ru.bvn13.voidforum.utils.DTOUtil;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Date;
import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
@Controller("accountUploadController")
@RequestMapping("account/files")
public class StoredFileController {
private static final int PAGE_SIZE = 20;
@Autowired
private FileStorageService storageService;
@Autowired
private StoredFileRepository storedFileRepository;
@Autowired
private UserService userService;
@GetMapping("")
public String index(@RequestParam(defaultValue = "0") int page, Model model) {
Page<StoredFile> files = storedFileRepository.findAllByUserOrderByIdDesc(userService.currentUser(), new PageRequest(page, PAGE_SIZE, Sort.Direction.DESC, "id"));
model.addAttribute("totalPages", files.getTotalPages());
model.addAttribute("page", page);
model.addAttribute("files", files);
return "account/files/index";
}
@PostMapping("/upload") //new annotation since 4.3
public String upload(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes) {
if (file.isEmpty()) {
redirectAttributes.addFlashAttribute("uploadStatus", "Please select a file to upload");
return "redirect:/account/files/status";
}
String message = "";
try {
// Get the file and save it somewhere
byte[] bytes = file.getBytes();
this.storageService.storeFile(userService.currentUser(), file.getOriginalFilename(), bytes);
message = "You successfully uploaded '" + file.getOriginalFilename() + "'";
redirectAttributes.addFlashAttribute("uploadStatus", message);
} catch (Exception e) {
e.printStackTrace();
message = "Internal server error occured";
redirectAttributes.addFlashAttribute("uploadStatus", message);
}
return "redirect:/account/files/status";
}
@GetMapping("/status")
public String uploadStatus() {
return "account/files/status";
}
@GetMapping(value = "/{fileId:[\\d]+}/edit")
public String editFileById(@PathVariable Long fileId, Model model) {
Assert.notNull(fileId);
StoredFile file = this.storageService.getFileById(fileId);
if (file == null) {
//response.sendError(404, String.format("File %s not found", fileId));
throw new NotFoundException(String.format("File with id %s not found", fileId));
}
StoredFileForm fileForm = DTOUtil.map(file, StoredFileForm.class);
model.addAttribute("file", file);
model.addAttribute("fileForm", fileForm);
return "account/files/edit";
}
@PostMapping(value = "/{fileId:[\\d]+}")
public String saveFile(@PathVariable Long fileId, @Valid StoredFileForm fileForm, Errors errors) {
Assert.notNull(fileId);
if (errors.hasErrors()) {
return String.format("account/files/%d/edit", fileId);
}
StoredFile storedFile = this.storedFileRepository.findById(fileId);
DTOUtil.mapTo(fileForm, storedFile);
storedFile.setUser(this.userService.currentUser());
storedFile.setUpdatedAt(new Date());
this.storedFileRepository.save(storedFile);
return "redirect:/account/files";
}
@RequestMapping(value = "{fileId:[0-9]+}/delete", method = {DELETE, POST})
public String deletePost(@PathVariable Long fileId){
try {
this.storageService.deleteFileById(fileId);
} catch (IOException e) {
e.printStackTrace();
}
return "redirect:/account/files";
}
}

View File

@ -0,0 +1,71 @@
package ru.bvn13.voidforum.controllers.account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.Assert;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import ru.bvn13.voidforum.forms.UserForm;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.repositories.UserRepository;
import ru.bvn13.voidforum.services.UserService;
import ru.bvn13.voidforum.support.web.MessageHelper;
import javax.validation.Valid;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
/**
* bvn13 <mail4bvn@gmail.com>.
*/
@Controller("accountUserController")
@RequestMapping("account/profile")
public class UserController {
private UserService userService;
private UserRepository userRepository;
@Autowired
public UserController(UserService userService, UserRepository userRepository){
this.userService = userService;
this.userRepository = userRepository;
}
@RequestMapping()
public String profile(Model model){
model.addAttribute("user", userService.currentUser());
return "account/users/profile";
}
@RequestMapping(value = "{userId:[0-9]+}", method = POST)
public String update(@PathVariable Long userId, @Valid UserForm userForm, Errors errors, RedirectAttributes ra){
User user = userRepository.findOne(userId);
Assert.notNull(user);
if (!userService.currentUser().getId().equals(userId)) {
throw new AccessDeniedException("You are not allowed here");
}
if (errors.hasErrors()){
// do something
return "account/users/profile";
}
if (!userForm.getNewPassword().isEmpty()){
if (!userService.changePassword(user, userForm.getPassword(), userForm.getNewPassword()))
MessageHelper.addErrorAttribute(ra, "Change password failed.");
else
MessageHelper.addSuccessAttribute(ra, "Change password successfully.");
}
return "redirect:/account/profile";
}
}

View File

@ -0,0 +1,60 @@
package ru.bvn13.voidforum.controllers.admin;
import ru.bvn13.voidforum.forms.SettingsForm;
import ru.bvn13.voidforum.services.AppSetting;
import ru.bvn13.voidforum.support.web.MessageHelper;
import ru.bvn13.voidforum.utils.DTOUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.validation.Valid;
/**
* bvn13 <mail4bvn@gmail.com>
*/
@Controller
@RequestMapping("admin")
public class AdminController {
private AppSetting appSetting;
@Autowired
public AdminController( AppSetting appSetting){
this.appSetting = appSetting;
}
@RequestMapping("")
public String index(){
return "admin/home/index";
}
@RequestMapping(value = "settings")
public String settings(Model model){
SettingsForm settingsForm = DTOUtil.map(appSetting, SettingsForm.class);
model.addAttribute("settings", settingsForm);
return "admin/home/settings";
}
@RequestMapping(value = "settings", method = RequestMethod.POST)
public String updateSettings(@Valid SettingsForm settingsForm, Errors errors, Model model, RedirectAttributes ra){
if (errors.hasErrors()){
return "admin/settings";
} else {
appSetting.setSiteName(settingsForm.getSiteName());
appSetting.setSiteSlogan(settingsForm.getSiteSlogan());
appSetting.setPageSize(settingsForm.getPageSize());
appSetting.setStoragePath(settingsForm.getStoragePath());
appSetting.setMainUri(settingsForm.getMainUri());
MessageHelper.addSuccessAttribute(ra, "Update settings successfully.");
return "redirect:settings";
}
}
}

View File

@ -0,0 +1,184 @@
package ru.bvn13.voidforum.controllers.admin;
import ru.bvn13.voidforum.forms.PostForm;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.models.support.*;
import ru.bvn13.voidforum.repositories.PostRepository;
import ru.bvn13.voidforum.repositories.UserRepository;
import ru.bvn13.voidforum.services.PostService;
import ru.bvn13.voidforum.services.PrivilegeService;
import ru.bvn13.voidforum.services.UserService;
import ru.bvn13.voidforum.utils.DTOUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.Valid;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.springframework.web.bind.annotation.RequestMethod.*;
/**
* bvn13 <mail4bvn@gmail.com>
*/
@Controller("adminPostController")
@RequestMapping("admin/posts")
public class PostController {
@Autowired
private PostRepository postRepository;
@Autowired
private PostService postService;
@Autowired
private UserRepository userRepository;
@Autowired
private UserService userService;
private static final int PAGE_SIZE = 20;
@RequestMapping(value = "")
public String index(@RequestParam(defaultValue = "0") int page, Model model) {
Page<Post> posts = postRepository.findAll(new PageRequest(page, PAGE_SIZE, Sort.Direction.DESC, "id"));
model.addAttribute("totalPages", posts.getTotalPages());
model.addAttribute("page", page);
model.addAttribute("posts", posts);
return "admin/posts/index";
}
private String makeFormPostCreation(Model model) {
PostForm postForm = DTOUtil.map(new Post(), PostForm.class);
postForm.init();
return this.makeFormPostCreation(model, postForm);
}
private String makeFormPostCreation(Model model, PostForm postForm) {
User user = userService.currentUser();
List<PostFormat> availableFormats = userService.getAvailablePostFormats(user);
model.addAttribute("postForm", postForm);
model.addAttribute("postFormats", availableFormats);
model.addAttribute("postStatus", PostStatus.values());
model.addAttribute("seoOgLocales", OgLocale.values());
model.addAttribute("seoOgTypes", OgType.values());
return "admin/posts/new";
}
@RequestMapping(value = "new")
public String newPost(Model model){
return this.makeFormPostCreation(model);
}
private String makeFormPostEdition(Long postId, Model model) {
return this.makeFormPostEdition(postId, model, null);
}
private String makeFormPostEdition(Long postId, Model model, PostForm postForm) {
Post post = postRepository.findOne(postId);
if (postForm == null) {
postForm = DTOUtil.map(post, PostForm.class);
}
postForm.init();
DTOUtil.mapTo(post, postForm);
postForm.initFromPost(post, postService.getTagNames(post.getTags()));
User user = userService.currentUser();
List<PostFormat> availableFormats = userService.getAvailablePostFormats(user);
model.addAttribute("post", post);
model.addAttribute("postForm", postForm);
model.addAttribute("postFormats", availableFormats);
model.addAttribute("postStatus", PostStatus.values());
model.addAttribute("seoOgLocales", OgLocale.values());
model.addAttribute("seoOgTypes", OgType.values());
return "admin/posts/edit";
}
@RequestMapping(value = "{postId:[0-9]+}/edit")
public String editPost(@PathVariable Long postId, Model model){
return this.makeFormPostEdition(postId, model);
}
@RequestMapping(value = "{postId:[0-9]+}/delete", method = {DELETE, POST})
public String deletePost(@PathVariable Long postId){
postService.deletePost(postRepository.findOne(postId));
return "redirect:/admin/posts";
}
@RequestMapping(value = "{postId:[0-9]+}/censore", method = {PUT, POST})
public String censorePost(@PathVariable Long postId){
postService.censorePost(postRepository.findOne(postId));
return "redirect:/admin/posts";
}
@RequestMapping(value = "", method = POST)
public String create(Principal principal, @Valid PostForm postForm, Errors errors, Model model){
if (errors.hasErrors()) {
Map<String, WebError> webErrors = new HashMap<>();
errors.getAllErrors().forEach(e -> {
String field = ((FieldError)e).getField();
webErrors.put(field, new WebError(field, e.getDefaultMessage()));
});
model.addAttribute("errors", webErrors);
return this.makeFormPostCreation(model, postForm);
} else {
Post post = DTOUtil.map(postForm, Post.class);
post.setUser(userRepository.findByEmail(principal.getName()));
post.setTags(postService.parseTagNames(postForm.getPostTags()));
postForm.fillOgFieldsInPost(post);
postService.createPost(post);
return "redirect:/admin/posts";
}
}
@RequestMapping(value = "{postId:[0-9]+}", method = {PUT, POST})
public String update(@PathVariable Long postId, @Valid PostForm postForm, Errors errors, Model model){
if (errors.hasErrors()){
Map<String, WebError> webErrors = new HashMap<>();
errors.getAllErrors().forEach(e -> {
String field = ((FieldError)e).getField();
webErrors.put(field, new WebError(field, e.getDefaultMessage()));
});
model.addAttribute("errors", webErrors);
return this.makeFormPostEdition(postId, model, postForm);
} else {
Post post = postRepository.findOne(postId);
DTOUtil.mapTo(postForm, post);
post.setTags(postService.parseTagNames(postForm.getPostTags()));
postForm.fillOgFieldsInPost(post);
postService.updatePost(post);
return "redirect:/admin/posts";
}
}
}

View File

@ -0,0 +1,70 @@
package ru.bvn13.voidforum.controllers.admin;
import ru.bvn13.voidforum.forms.SeoRobotAgentForm;
import ru.bvn13.voidforum.models.SeoRobotAgent;
import ru.bvn13.voidforum.repositories.SeoRobotAgentRepository;
import ru.bvn13.voidforum.utils.DTOUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.Assert;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.validation.Valid;
@Controller
@RequestMapping(value = "/admin/robotsAgents")
public class SeoRobotAgentController {
@Autowired
private SeoRobotAgentRepository seoRobotAgentRepository;
@GetMapping()
public String getSeoRobotsAgents(Model model) {
model.addAttribute("records", this.seoRobotAgentRepository.findAll());
model.addAttribute("form", new SeoRobotAgentForm());
return "admin/robotsAgents/index";
}
@GetMapping(value = "/{recordId:[\\d]+}/edit")
public String editSeoRobotAgent(@PathVariable Long recordId, Model model) {
SeoRobotAgent ua = this.seoRobotAgentRepository.findOne(recordId);
Assert.notNull(ua);
model.addAttribute("form", DTOUtil.map(ua, SeoRobotAgentForm.class));
return "admin/robotsAgents/edit";
}
@PostMapping(value = "/{recordId:[\\d]+}/edit")
public String saveSeoRobotAgent(@PathVariable Long recordId, @Valid SeoRobotAgentForm form, Errors errors) {
SeoRobotAgent ua = null;
if (recordId.equals(0L)) {
ua = new SeoRobotAgent();
} else {
ua = this.seoRobotAgentRepository.findOne(recordId);
}
Assert.notNull(ua);
DTOUtil.mapTo(form, ua);
this.seoRobotAgentRepository.save(ua);
return "redirect:/admin/robotsAgents";
}
@PostMapping(value = "/{recordId:[\\d]+}/delete")
public String deleteSeoRobotAgent(@PathVariable Long recordId) {
this.seoRobotAgentRepository.delete(recordId);
return "redirect:/admin/robotsAgents";
}
}

View File

@ -0,0 +1,135 @@
package ru.bvn13.voidforum.controllers.admin;
import ru.bvn13.voidforum.error.NotFoundException;
import ru.bvn13.voidforum.forms.StoredFileForm;
import ru.bvn13.voidforum.models.StoredFile;
import ru.bvn13.voidforum.repositories.StoredFileRepository;
import ru.bvn13.voidforum.services.FileStorageService;
import ru.bvn13.voidforum.services.UserService;
import ru.bvn13.voidforum.utils.DTOUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.Assert;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Date;
import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
@Controller("adminUploadController")
@RequestMapping("admin/files")
public class StoredFileController {
private static final int PAGE_SIZE = 20;
@Autowired
private FileStorageService storageService;
@Autowired
private StoredFileRepository storedFileRepository;
@Autowired
private UserService userService;
@GetMapping("")
public String index(@RequestParam(defaultValue = "0") int page, Model model) {
Page<StoredFile> files = storedFileRepository.findAll(new PageRequest(page, PAGE_SIZE, Sort.Direction.DESC, "id"));
model.addAttribute("totalPages", files.getTotalPages());
model.addAttribute("page", page);
model.addAttribute("files", files);
return "admin/files/index";
}
@PostMapping("/upload") //new annotation since 4.3
public String upload(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes) {
if (file.isEmpty()) {
redirectAttributes.addFlashAttribute("uploadStatus", "Please select a file to upload");
return "redirect:/admin/files/status";
}
String message = "";
try {
// Get the file and save it somewhere
byte[] bytes = file.getBytes();
this.storageService.storeFile(userService.currentUser(), file.getOriginalFilename(), bytes);
message = "You successfully uploaded '" + file.getOriginalFilename() + "'";
redirectAttributes.addFlashAttribute("uploadStatus", message);
} catch (Exception e) {
e.printStackTrace();
message = "Internal server error occured";
redirectAttributes.addFlashAttribute("uploadStatus", message);
}
return "redirect:/admin/files/status";
}
@GetMapping("/status")
public String uploadStatus() {
return "admin/files/status";
}
@GetMapping(value = "/{fileId:[\\d]+}/edit")
public String editFileById(@PathVariable Long fileId, Model model) {
Assert.notNull(fileId);
StoredFile file = this.storageService.getFileById(fileId);
if (file == null) {
//response.sendError(404, String.format("File %s not found", fileId));
throw new NotFoundException(String.format("File with id %s not found", fileId));
}
StoredFileForm fileForm = DTOUtil.map(file, StoredFileForm.class);
model.addAttribute("file", file);
model.addAttribute("fileForm", fileForm);
return "admin/files/edit";
}
@PostMapping(value = "/{fileId:[\\d]+}")
public String saveFile(@PathVariable Long fileId, @Valid StoredFileForm fileForm, Errors errors) {
Assert.notNull(fileId);
if (errors.hasErrors()) {
return String.format("admin/files/%d/edit", fileId);
}
StoredFile storedFile = this.storedFileRepository.findById(fileId);
DTOUtil.mapTo(fileForm, storedFile);
storedFile.setUser(this.userService.currentUser());
storedFile.setUpdatedAt(new Date());
this.storedFileRepository.save(storedFile);
return "redirect:/admin/files";
}
@RequestMapping(value = "{fileId:[0-9]+}/delete", method = {DELETE, POST})
public String deletePost(@PathVariable Long fileId){
try {
this.storageService.deleteFileById(fileId);
} catch (IOException e) {
e.printStackTrace();
}
return "redirect:/admin/files";
}
}

View File

@ -0,0 +1,66 @@
package ru.bvn13.voidforum.controllers.admin;
import ru.bvn13.voidforum.forms.UserForm;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.repositories.UserRepository;
import ru.bvn13.voidforum.services.UserService;
import ru.bvn13.voidforum.support.web.MessageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.Assert;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.validation.Valid;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
/**
* bvn13 <mail4bvn@gmail.com>.
*/
@Controller("adminUserController")
@RequestMapping("admin/users")
public class UserController {
private UserService userService;
private UserRepository userRepository;
@Autowired
public UserController(UserService userService, UserRepository userRepository){
this.userService = userService;
this.userRepository = userRepository;
}
@RequestMapping("profile")
public String profile(Model model){
model.addAttribute("user", userService.currentUser());
return "admin/users/profile";
}
@RequestMapping(value = "{userId:[0-9]+}", method = POST)
public String update(@PathVariable Long userId, @Valid UserForm userForm, Errors errors, RedirectAttributes ra){
User user = userRepository.findOne(userId);
Assert.notNull(user);
if (errors.hasErrors()){
// do something
return "admin/users/profile";
}
if (!userForm.getNewPassword().isEmpty()){
if (!userService.changePassword(user, userForm.getPassword(), userForm.getNewPassword()))
MessageHelper.addErrorAttribute(ra, "Change password failed.");
else
MessageHelper.addSuccessAttribute(ra, "Change password successfully.");
}
return "redirect:profile";
}
}

View File

@ -0,0 +1,32 @@
package ru.bvn13.voidforum.controllers.seo;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.services.PostService;
import ru.bvn13.voidforum.services.SeoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
@RequestMapping(value = {"/seo", ""})
public class SitemapController {
@Autowired
private PostService postService;
@Autowired
private SeoService seoService;
@GetMapping(value = "/sitemap", produces = MediaType.APPLICATION_XML_VALUE)
public @ResponseBody
String getSiteMap() {
List<Post> posts = this.postService.getAllPublishedPosts();
return this.seoService.createSitemap(posts);
}
}

View File

@ -0,0 +1,14 @@
package ru.bvn13.voidforum.error;
import lombok.Getter;
public class EmailExistsException extends Exception {
@Getter
private String message;
public EmailExistsException(){}
public EmailExistsException(String message){
this.message = message;
}
}

View File

@ -0,0 +1,53 @@
package ru.bvn13.voidforum.error;
import com.google.common.base.Throwables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
/**
* General error handler for the application.
*/
@ControllerAdvice
class ExceptionHandlerController {
private static final Logger logger = LoggerFactory.getLogger(ExceptionHandlerController.class);
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(NotFoundException.class)
public ModelAndView notFound(HttpServletRequest request, NotFoundException exception){
String uri = request.getRequestURI();
logger.error("Request page: " + uri + " raised NotFoundException : " + exception);
ModelAndView model = new ModelAndView("error/general");
model.addObject("status", HttpStatus.NOT_FOUND.value());
model.addObject("error", HttpStatus.NOT_FOUND.getReasonPhrase());
model.addObject("path", uri);
model.addObject("customMessage", exception.getMessage());
return model;
}
/**
* Handle all exceptions
*/
// @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
@ExceptionHandler(Exception.class)
public ModelAndView exception(HttpServletRequest request, Exception exception) {
String uri = request.getRequestURI();
logger.error("Request page: " + uri + " raised exception : " + exception);
ModelAndView model = new ModelAndView("error/general");
model.addObject("error", Throwables.getRootCause(exception).getMessage());
model.addObject("status", Throwables.getRootCause(exception).getCause());
model.addObject("path", uri);
model.addObject("customMessage", exception.getMessage());
return model;
}
}

View File

@ -0,0 +1,15 @@
package ru.bvn13.voidforum.error;
import lombok.Getter;
public class NicknameExistsException extends Exception {
@Getter
private String message;
public NicknameExistsException(){}
public NicknameExistsException(String message){
this.message = message;
}
}

View File

@ -0,0 +1,25 @@
package ru.bvn13.voidforum.error;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* @author Raysmond<i@raysmond.com>
*/
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public final class NotFoundException extends RuntimeException {
private String message;
public NotFoundException(){
}
public NotFoundException(String message){
this.message = message;
}
@Override
public String getMessage(){
return message;
}
}

View File

@ -0,0 +1,29 @@
package ru.bvn13.voidforum.forms;
import lombok.Data;
import ru.bvn13.voidforum.models.Comment;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.models.support.CommentFormat;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* Created by bvn13 on 08.12.2017.
*/
@Data
public class CommentForm {
@NotNull
private Integer postId;
private Integer parentCommentId;
@NotEmpty
private String content;
@NotNull
private CommentFormat commentFormat;
}

View File

@ -0,0 +1,13 @@
package ru.bvn13.voidforum.forms;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
public class LikeForm {
@NotNull
private String sympathy = "0";
}

View File

@ -0,0 +1,117 @@
package ru.bvn13.voidforum.forms;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.SeoPostData;
import ru.bvn13.voidforum.models.support.OgLocale;
import ru.bvn13.voidforum.models.support.OgType;
import ru.bvn13.voidforum.models.support.PostFormat;
import ru.bvn13.voidforum.models.support.PostStatus;
import javax.validation.constraints.NotNull;
/**
* bvn13 <mail4bvn@gmail.com>
*/
@Data
public class PostForm {
@NotEmpty
private String title;
@NotEmpty
private String content;
@NotNull
private PostFormat postFormat;
@NotNull
private PostStatus postStatus;
@NotNull
private String permalink;
@NotNull
private String postTags;
@NotNull
private String seoKeywords;
@NotNull
private String seoDescription;
// @NotNull
// private String seoOgTitle;
@NotNull
private OgType seoOgType;
@NotNull
private String seoOgImage;
@NotNull
private String seoOgVideo;
@NotNull
private OgLocale seoOgLocale;
@NotNull
private Boolean deletedMark;
@NotNull
private Boolean censored;
public void init() {
this.setTitle("");
this.setPermalink("");
this.setContent("");
this.setPostTags("");
this.setPostStatus(PostStatus.DRAFT);
this.setPostFormat(PostFormat.MARKDOWN);
this.setSeoKeywords("");
this.setSeoOgImage("");
this.setSeoOgLocale(OgLocale.ru_RU);
//this.setSeoOgTitle("");
this.setSeoOgType(OgType.WEBSITE);
this.setSeoOgVideo("");
this.setDeletedMark(false);
this.setCensored(false);
}
public void initFromPost(Post post) {
if (post.getSeoData() != null) {
this.setSeoOgImage(post.getSeoData().getOgImage());
this.setSeoOgVideo(post.getSeoData().getOgVideo());
this.setSeoOgLocale(post.getSeoData().getOgLocale());
this.setSeoOgType(post.getSeoData().getOgType());
} else {
this.setSeoOgImage("");
this.setSeoOgLocale(OgLocale.ru_RU);
//this.setSeoOgTitle("");
this.setSeoOgType(OgType.WEBSITE);
this.setSeoOgVideo("");
}
}
public void initFromPost(Post post, String postTags) {
this.initFromPost(post);
this.setPostTags(postTags);
}
public void fillOgFieldsInPost(Post post) {
SeoPostData data = null;
if (post.getSeoData() == null) {
data = new SeoPostData();
} else {
data = post.getSeoData();
}
data.setOgImage(this.seoOgImage);
data.setOgLocale(this.seoOgLocale);
data.setOgTitle(this.title);
data.setOgType(this.seoOgType);
data.setOgVideo(this.seoOgVideo);
post.setSeoData(data);
}
}

View File

@ -0,0 +1,30 @@
package ru.bvn13.voidforum.forms;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* bvn13 <mail4bvn@gmail.com>.
*/
@Data
public class RegistrationForm {
@Email(message = "Email should be valid")
@NotBlank(message = "Specify your email")
private String username;
@NotBlank(message = "Specify your nickname")
private String nickname;
@NotBlank(message = "Specify your password")
private String password;
@NotBlank(message = "Verify your password")
private String passwordCheck;
}

View File

@ -0,0 +1,18 @@
package ru.bvn13.voidforum.forms;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
public class SeoRobotAgentForm {
@NotNull
private Long id = 0L;
@NotNull
private String userAgent = "";
@NotNull
private Boolean isRegexp = false;
}

View File

@ -0,0 +1,30 @@
package ru.bvn13.voidforum.forms;
import lombok.Data;
import org.hibernate.validator.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* bvn13 <mail4bvn@gmail.com>
*/
@Data
public class SettingsForm {
@NotEmpty
@NotNull
private String siteName;
@NotNull
private String siteSlogan;
@NotNull
private Integer pageSize;
@NotNull
private String storagePath;
@NotNull
private String mainUri;
}

View File

@ -0,0 +1,22 @@
package ru.bvn13.voidforum.forms;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
public class StoredFileForm {
@NotNull
private String title;
@NotNull
private String name;
@NotNull
private String path;
@NotNull
private Long size;
}

View File

@ -0,0 +1,22 @@
package ru.bvn13.voidforum.forms;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Email;
/**
* bvn13 <mail4bvn@gmail.com>.
*/
@Data
public class UserForm {
@NotBlank(message = "Enter your password")
private String password;
@NotBlank(message = "Specify your new password")
private String newPassword;
}

View File

@ -0,0 +1,80 @@
package ru.bvn13.voidforum.models;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
/**
* An abstract base model class for entities
*
* @author bvn13 <mail4bvn@gmail.com>
*/
@MappedSuperclass
public abstract class BaseModel implements Comparable<BaseModel>, Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(nullable = false)
private Date createdAt;
@Column(nullable = false)
private Date updatedAt;
@PrePersist
public void prePersist(){
createdAt = updatedAt = new Date();
}
@PreUpdate
public void preUpdate(){
updatedAt = new Date();
}
@Override
public int compareTo(BaseModel o) {
return this.getId().compareTo(o.getId());
}
public boolean equals(Object other) {
if (other == null || other.getClass() != this.getClass())
return false;
return this.getId().equals(((BaseModel) other).getId());
}
public int hashCode() {
return new HashCodeBuilder().append(getId()).toHashCode();
}
public Long getId() {
return id;
}
public void setId(Long _id) {
id = _id;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
}

View File

@ -0,0 +1,49 @@
package ru.bvn13.voidforum.models;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Type;
import ru.bvn13.voidforum.models.support.CommentFormat;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Collection;
@Entity
@Table(name = "comments")
@Getter
@Setter
public class Comment extends BaseModel {
@ManyToOne
private User user;
@ManyToOne
private Post post;
@ManyToOne
private Comment parentComment;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "parentComment", cascade = CascadeType.REMOVE)
private Collection<Comment> children = new ArrayList<>();
@Type(type="text")
@Getter
@Setter
private String content;
@Type(type = "text")
private String renderedContent;
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private CommentFormat commentFormat = CommentFormat.MARKDOWN;
@Column(nullable = false, columnDefinition = "boolean DEFAULT false")
private Boolean deletedMark;
@Column(nullable = false, columnDefinition = "integer DEFAULT 0")
private Integer depth;
}

View File

@ -0,0 +1,32 @@
package ru.bvn13.voidforum.models;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "posts_likes")
@Getter
@Setter
public class Like extends BaseModel {
@ManyToOne
private User user;
@ManyToOne
private Post post;
@Column(nullable = false)
private Integer sympathy;
@Column(nullable = false)
private String clientIp;
@Column(nullable = false, columnDefinition = "boolean default false")
private Boolean isAdmin;
}

View File

@ -0,0 +1,101 @@
package ru.bvn13.voidforum.models;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Type;
import org.springframework.util.StringUtils;
import ru.bvn13.voidforum.models.support.PostFormat;
import ru.bvn13.voidforum.models.support.PostStatus;
import ru.bvn13.voidforum.models.support.PostType;
import javax.persistence.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* bvn13 <mail4bvn@gmail.com>
*/
@Entity
@Table(name = "posts")
@Getter
@Setter
public class Post extends BaseModel {
private static final SimpleDateFormat SLUG_DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd");
@ManyToOne
private User user;
@Column(nullable = false)
private String title;
@Type(type="text")
private String content;
@Type(type = "text")
private String renderedContent;
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private PostStatus postStatus = PostStatus.PUBLISHED;
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private PostFormat postFormat = PostFormat.MARKDOWN;
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private PostType postType = PostType.POST;
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "posts_tags",
joinColumns = {@JoinColumn(name = "post_id", nullable = false, updatable = false)},
inverseJoinColumns = {@JoinColumn(name = "tag_id", nullable = false, updatable = false)}
)
private Set<Tag> tags = new HashSet<>();
@Column(nullable = false, columnDefinition = "character varying DEFAULT ''")
private String seoKeywords = "";
@Column(nullable = false, columnDefinition = "character varying DEFAULT ''")
private String seoDescription = "";
@OneToOne
private SeoPostData seoData;
@Type(type="text")
private String permalink;
public void setPermalink(String permalink){
String token = permalink.toLowerCase().replace("\n", " ").replaceAll("[^a-z\\d\\s]", " ");
this.permalink = StringUtils.arrayToDelimitedString(StringUtils.tokenizeToStringArray(token, " "), "-");
}
private Long visitsCount = 0L;
public Long getVisitsCount() {
if (this.visitsCount == null) return 0L;
else return this.visitsCount;
}
private Integer sympathyCount = 0;
public Integer getSympathyCount() {
if (this.sympathyCount == null) return 0;
else return this.sympathyCount;
}
@OneToMany(fetch = FetchType.LAZY, mappedBy = "post", cascade = CascadeType.REMOVE)
private Collection<Comment> comments = new ArrayList<>();
@JsonInclude
@Transient
private Comment lastComment;
@Column(nullable = false, columnDefinition = "boolean DEFAULT false")
private Boolean deletedMark;
@Column(nullable = false, columnDefinition = "boolean DEFAULT false")
private Boolean censored;
}

View File

@ -0,0 +1,26 @@
package ru.bvn13.voidforum.models;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.Collection;
@Entity
@Table(name = "privileges")
@Getter
@Setter
public class Privilege extends BaseModel {
private String name;
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "privileges")
private Collection<Role> roles;
public Privilege() {}
public Privilege(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,34 @@
package ru.bvn13.voidforum.models;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.Collection;
@Entity
@Table(name = "roles")
@Getter
@Setter
public class Role extends BaseModel {
private String name;
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "roles")
private Collection<User> users;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "roles_privileges",
joinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "privilege_id", referencedColumnName = "id")}
)
private Collection<Privilege> privileges;
public Role() {}
public Role(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,42 @@
package ru.bvn13.voidforum.models;
import lombok.Getter;
import lombok.Setter;
import ru.bvn13.voidforum.models.support.OgLocale;
import ru.bvn13.voidforum.models.support.OgType;
import javax.persistence.*;
@Entity
@Table(name = "seo_posts_data")
@Getter
@Setter
public class SeoPostData extends BaseModel {
@OneToOne(fetch = FetchType.LAZY, mappedBy = "seoData", cascade = CascadeType.ALL)
private Post post;
@Column
private String ogTitle = "";
@Column
@Enumerated(EnumType.STRING)
private OgType ogType = OgType.ARTICLE;
@Column
private String ogImage = "";
@Column
private String ogVideo = "";
@Column
@Enumerated(EnumType.STRING)
private OgLocale ogLocale = OgLocale.en_EN;
/*@Column
private String ogUrl;
@Column
private String ogDescription;*/
}

View File

@ -0,0 +1,30 @@
package ru.bvn13.voidforum.models;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name = "seo_robots_agents")
@Getter
@Setter
public class SeoRobotAgent extends BaseModel {
@Column(nullable = false)
private String userAgent;
@Column(nullable = false, columnDefinition = "boolean default false")
private Boolean isRegexp;
public SeoRobotAgent() {
}
public SeoRobotAgent(String userAgent) {
this.userAgent = userAgent;
}
}

View File

@ -0,0 +1,30 @@
package ru.bvn13.voidforum.models;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.Table;
import java.io.Serializable;
/**
* A generic setting model
*
* bvn13 <mail4bvn@gmail.com>
*/
@Entity
@Table(name = "settings")
@Getter
@Setter
public class Setting extends BaseModel{
@Column(name = "_key", unique = true, nullable = false)
private String key;
@Lob
@Column(name = "_value")
private Serializable value;
}

View File

@ -0,0 +1,64 @@
package ru.bvn13.voidforum.models;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "stored_files")
@Getter
@Setter
public class StoredFile extends BaseModel {
@ManyToOne
private User user;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String path;
@Column(columnDefinition = "bigint default 0")
private Long size;
public String getSizeFormatted() {
double bytes = this.getSize();
double kilobytes = (bytes / 1024);
double megabytes = (kilobytes / 1024);
double gigabytes = (megabytes / 1024);
double terabytes = (gigabytes / 1024);
double petabytes = (terabytes / 1024);
double exabytes = (petabytes / 1024);
double zettabytes = (exabytes / 1024);
double yottabytes = (zettabytes / 1024);
if (Math.floor(yottabytes) > 0d) {
return String.format("%.3f Yb", yottabytes);
} else if (Math.floor(zettabytes) > 0d) {
return String.format("%.3f Zb", zettabytes);
} else if (Math.floor(exabytes) > 0d) {
return String.format("%.3f Eb", exabytes);
} else if (Math.floor(petabytes) > 0d) {
return String.format("%.3f Pb", petabytes);
} else if (Math.floor(terabytes) > 0d) {
return String.format("%.3f Tb", terabytes);
} else if (Math.floor(gigabytes) > 0d) {
return String.format("%.3f Gb", gigabytes);
} else if (Math.floor(megabytes) > 0d) {
return String.format("%.3f Mb", megabytes);
} else if (Math.floor(kilobytes) > 0d) {
return String.format("%.3f Kb", kilobytes);
} else {
return String.format("%d bytes", (int)bytes);
}
}
}

View File

@ -0,0 +1,32 @@
package ru.bvn13.voidforum.models;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
/**
* bvn13 <mail4bvn@gmail.com>.
*/
@Entity
@Table(name = "tags")
@Getter
@Setter
public class Tag extends BaseModel {
@Column(nullable = false, unique = true)
private String name;
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "tags")
private List<Post> posts = new ArrayList<>();
public Tag(){
}
public Tag(String name){
this.setName(name);
}
}

View File

@ -0,0 +1,70 @@
package ru.bvn13.voidforum.models;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.Setter;
import ru.bvn13.voidforum.services.RoleService;
import ru.bvn13.voidforum.services.UserService;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicReference;
/**
* bvn13 <mail4bvn@gmail.com>
*/
@Entity
@Table(name = "users")
@Getter
@Setter
public class User extends BaseModel {
@Column(unique = true)
private String email;
@Column(unique = true)
private String nickname;
@JsonIgnore
private String password;
private Boolean disabled;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "users_roles",
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")}
)
private Collection<Role> roles;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.REMOVE)
private Collection<Post> posts = new ArrayList<>();
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.REMOVE)
private Collection<StoredFile> storedFiles = new ArrayList<>();
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.REMOVE)
private Collection<Comment> comments = new ArrayList<>();
public User() {
}
/*
public Boolean isAdmin() {
return UserService.hasRole(this, RoleService.ROLE_ADMIN) || UserService.hasRole(this, RoleService.ROLE_OWNER);
}
public Boolean isOwner() {
return UserService.hasRole(this, RoleService.ROLE_OWNER);
}
*/
public User(String email, String nickname, String password, String role) {
this.email = email;
this.nickname = nickname;
this.password = password;
this.roles.add(new Role(role));
}
}

View File

@ -0,0 +1,32 @@
package ru.bvn13.voidforum.models;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "visits")
@Getter
@Setter
public class Visit extends BaseModel {
@ManyToOne
private User user;
@Column(nullable = false)
private String clientIp;
@ManyToOne
private Post post;
@Column(nullable = false, columnDefinition = "boolean default false")
private Boolean isAdmin;
@Column
private String userAgent;
}

View File

@ -0,0 +1,27 @@
package ru.bvn13.voidforum.models.support;
public enum CommentFormat {
HTML("Html"),
MARKDOWN("Markdown");
private String name;
CommentFormat(String name){
this.name = name;
}
public String getName(){
return name;
}
public String getId() {
return name();
}
@Override
public String toString() {
return getName();
}
}

View File

@ -0,0 +1,40 @@
package ru.bvn13.voidforum.models.support;
import java.util.Hashtable;
public class HttpContentTypeSerializer {
private static final Hashtable<String, String> variants = new Hashtable<String, String>();
private static final String defaultVariant = "application/octet-stream";
static {
variants.put("aac", "audio/aac");
variants.put("abw", "application/x-abiword");
variants.put("", "");
variants.put("", "");
variants.put("", "");
variants.put("", "");
variants.put("", "");
variants.put("", "");
}
public static String getContentType(String fileName) {
if (fileName.isEmpty()) {
return defaultVariant;
}
String fileArray[]=fileName.split("\\.");
String extension = fileArray[fileArray.length-1];
if (variants.containsKey(extension)) {
return variants.get(extension);
}
return defaultVariant;
}
}

View File

@ -0,0 +1,31 @@
package ru.bvn13.voidforum.models.support;
public enum OgLocale {
en_EN("en_EN"),
ru_RU("ru_RU");
private String name;
OgLocale(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId(){
return name();
}
@Override
public String toString() {
return getName();
}
}

View File

@ -0,0 +1,27 @@
package ru.bvn13.voidforum.models.support;
public enum OgType {
WEBSITE("website"),
ARTICLE("article");
private String name;
OgType(String name){
this.name = name;
}
public String getName(){
return name;
}
public String getId() {
return name();
}
@Override
public String toString() {
return getName();
}
}

View File

@ -0,0 +1,28 @@
package ru.bvn13.voidforum.models.support;
/**
* bvn13 <mail4bvn@gmail.com>
*/
public enum PostFormat {
HTML("Html"),
MARKDOWN("Markdown");
private String name;
PostFormat(String name){
this.name = name;
}
public String getName(){
return name;
}
public String getId() {
return name();
}
@Override
public String toString() {
return getName();
}
}

View File

@ -0,0 +1,32 @@
package ru.bvn13.voidforum.models.support;
/**
* bvn13 <mail4bvn@gmail.com>
*/
public enum PostStatus {
DRAFT("Draft"),
PUBLISHED("Published");
private String name;
PostStatus(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId(){
return name();
}
@Override
public String toString() {
return getName();
}
}

View File

@ -0,0 +1,32 @@
package ru.bvn13.voidforum.models.support;
/**
* bvn13 <mail4bvn@gmail.com>
*/
public enum PostType {
PAGE("Page"),
POST("Post");
private String name;
PostType(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId(){
return name();
}
@Override
public String toString() {
return getName();
}
}

View File

@ -0,0 +1,21 @@
package ru.bvn13.voidforum.models.support;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
@Getter
@Setter
public class WebError implements Serializable {
private String field;
private String errorMessage;
public WebError(String field, String errorMessage) {
this.field = field;
this.errorMessage = errorMessage;
}
}

View File

@ -0,0 +1,18 @@
package ru.bvn13.voidforum.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import ru.bvn13.voidforum.models.Comment;
import ru.bvn13.voidforum.models.Post;
import java.util.List;
@Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {
List<Comment> findAllByPostOrderById(Post post);
List<Comment> findAllByPostAndParentCommentOrderById(Post post, Comment parentComment);
List<Comment> findAllByPostAndDeletedMarkOrderById(Post post, Boolean deletedMark);
List<Comment> findAllByPostAndParentCommentAndDeletedMarkOrderById(Post post, Comment parentComment, Boolean deletedMark);
}

View File

@ -0,0 +1,22 @@
package ru.bvn13.voidforum.repositories;
import ru.bvn13.voidforum.models.Like;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface LikeRepository extends JpaRepository<Like, Long> {
@Query("SELECT SUM(l.sympathy) FROM Like AS l WHERE l.user = :user AND l.post = :post GROUP BY l.user, l.post")
Integer getTotalLikesByUserAndPost(@Param("user") User user, @Param("post") Post post);
@Query("SELECT SUM(l.sympathy) FROM Like AS l WHERE l.post = :post GROUP BY l.post")
Integer getTotalLikesByPost(@Param("post") Post post);
@Query("SELECT SUM(l.sympathy) FROM Like AS l WHERE l.clientIp = :clientIp AND l.post = :post GROUP BY l.clientIp, l.post")
Integer getTotalLikesByClientIpAndPost(@Param("clientIp") String clientIp, @Param("post") Post post);
}

View File

@ -0,0 +1,49 @@
package ru.bvn13.voidforum.repositories;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.models.support.PostStatus;
import ru.bvn13.voidforum.models.support.PostType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* bvn13 <mail4bvn@gmail.com>
*/
@Repository
@Transactional
public interface PostRepository extends JpaRepository<Post, Long> {
Post findByPermalinkAndPostStatus(String permalink, PostStatus postStatus);
Post findByIdAndPostStatus(Long postId, PostStatus postStatus);
Page<Post> findAllByPostType(PostType postType, Pageable pageRequest);
Page<Post> findAllByPostTypeAndPostStatus(PostType postType, PostStatus postStatus, Pageable pageRequest);
Page<Post> findAllByPostTypeAndPostStatusAndDeletedMarkAndCensored(PostType postType, PostStatus postStatus, Boolean deletedMark, Boolean censored, Pageable pageRequest);
List<Post> findAllByPostTypeAndPostStatus(PostType postType, PostStatus postStatus);
@Query("SELECT p FROM Post p INNER JOIN p.tags t WHERE t.name = :tag")
Page<Post> findByTag(@Param("tag") String tag, Pageable pageable);
@Query("SELECT t.name, count(p) as tag_count from Post p " +
"INNER JOIN p.tags t " +
"WHERE p.postStatus = :status " +
"GROUP BY t.id " +
"ORDER BY tag_count DESC")
List<Object[]> countPostsByTags(@Param("status") PostStatus status);
Page<Post> findAllByPostTypeAndPostStatusAndDeletedMark(PostType postType, PostStatus postStatus, Boolean deletedMark, Pageable pageRequest);
Page<Post> findAllByUser(User user, Pageable pageRequest);
}

View File

@ -0,0 +1,22 @@
package ru.bvn13.voidforum.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import ru.bvn13.voidforum.models.Privilege;
import ru.bvn13.voidforum.models.Role;
import java.util.Collection;
import java.util.List;
public interface PrivilegeRepository extends JpaRepository<Privilege, Long> {
Privilege findByName(String name);
@Query("SELECT p FROM Privilege AS p " +
"INNER JOIN p.roles AS r " +
"WHERE r = :role")
List<Privilege> findAllByRole(@Param("role") Role role);
/*@Query("SELECT p FROM Privilege AS p " +
"INNER JOIN p.roles AS r " +
"WHERE r IN :roles")
List<Privilege> findAllByRolesList(@Param("roles") Collection<Role> roles);*/
}

View File

@ -0,0 +1,17 @@
package ru.bvn13.voidforum.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import ru.bvn13.voidforum.models.Role;
import ru.bvn13.voidforum.models.User;
import java.util.List;
public interface RoleRepository extends JpaRepository<Role, Long> {
Role findByName(String role_name);
@Query("SELECT r FROM Role AS r " +
"INNER JOIN r.users AS u " +
"WHERE u = :user")
List<Role> findAllByUser(@Param("user") User user);
}

View File

@ -0,0 +1,9 @@
package ru.bvn13.voidforum.repositories;
import ru.bvn13.voidforum.models.SeoPostData;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SeoPostDataRepository extends JpaRepository<SeoPostData, Long> {
}

View File

@ -0,0 +1,8 @@
package ru.bvn13.voidforum.repositories;
import ru.bvn13.voidforum.models.SeoRobotAgent;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SeoRobotAgentRepository extends JpaRepository<SeoRobotAgent, Long> {
}

View File

@ -0,0 +1,15 @@
package ru.bvn13.voidforum.repositories;
import ru.bvn13.voidforum.models.Setting;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
/**
* bvn13 <mail4bvn@gmail.com>
*/
@Repository
@Transactional
public interface SettingRepository extends JpaRepository<Setting, Long> {
Setting findByKey(String key);
}

View File

@ -0,0 +1,15 @@
package ru.bvn13.voidforum.repositories;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import ru.bvn13.voidforum.models.StoredFile;
import org.springframework.data.jpa.repository.JpaRepository;
import ru.bvn13.voidforum.models.User;
@Repository
public interface StoredFileRepository extends JpaRepository<StoredFile, Long> {
StoredFile findById(Long id);
StoredFile findByName(String name);
Page<StoredFile> findAllByUserOrderByIdDesc(User user, Pageable pageRequest);
}

View File

@ -0,0 +1,11 @@
package ru.bvn13.voidforum.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import ru.bvn13.voidforum.models.Tag;
/**
* bvn13 <mail4bvn@gmail.com>.
*/
public interface TagRepository extends JpaRepository<Tag, Long> {
Tag findByName(String name);
}

View File

@ -0,0 +1,29 @@
package ru.bvn13.voidforum.repositories;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import ru.bvn13.voidforum.models.Role;
import ru.bvn13.voidforum.models.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* bvn13 <mail4bvn@gmail.com>
*/
@Repository
@Transactional
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmail(String email);
User findByNickname(String nickname);
@Query("SELECT u FROM User AS u " +
"INNER JOIN u.roles AS r " +
"WHERE r.name = :roleName")
List<User> findAllByOneRoleByNameOrderById(@Param("roleName") String rname);
@Query("SELECT u FROM User AS u " +
"INNER JOIN u.roles AS r " +
"WHERE r = :role")
List<User> findAllByOneRoleOrderById(@Param("role") Role role);
}

View File

@ -0,0 +1,29 @@
package ru.bvn13.voidforum.repositories;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.Visit;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface VisitRepository extends JpaRepository<Visit, Long> {
@Query( "SELECT COUNT(DISTINCT v.clientIp) " +
"FROM Visit AS v " +
//"LEFT JOIN v.robotsAgents AS ra " +
//"ON v.userAgent LIKE concat('%', ra.userAgent, '%') "+
"WHERE v.post = :post AND v.isAdmin = FALSE " //+
//"AND ra.id IS NULL "
)
Long getUniquePostVisitsCount(@Param("post") Post post);
@Query( "SELECT v.clientIp, NULLIF(v.userAgent, '') " +
"FROM Visit AS v " +
"WHERE v.post = :post AND v.isAdmin = FALSE " +
"GROUP BY v.clientIp, NULLIF(v.userAgent, '') "
)
List<Object> getVisitsByPostAndIsAdminIsFalse(@Param("post") Post post);
}

View File

@ -0,0 +1,93 @@
package ru.bvn13.voidforum.services;
import com.domingosuarez.boot.autoconfigure.jade4j.JadeHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* bvn13 <mail4bvn@gmail.com>
*/
@JadeHelper("App")
@Service
public class AppSetting {
private SettingService settingService;
private String siteName = "void Forum();";
private String siteSlogan = "An interesting place to discover";
private Integer pageSize = 5;
private String storagePath = "/tmp";
private String mainUri = "http://localhost/";
public static final String SITE_NAME = "site_name";
public static final String SITE_SLOGAN = "site_slogan";
public static final String PAGE_SIZE = "page_size";
public static final String STORAGE_PATH = "storage_path";
public static final String MAIN_URI = "main_uri";
@Autowired
public AppSetting(SettingService settingService){
this.settingService = settingService;
}
public String getSiteName(){
return (String) settingService.get(SITE_NAME, siteName);
}
public void setSiteName(String siteName) {
this.siteName = siteName;
settingService.put(SITE_NAME, siteName);
}
public Integer getPageSize() {
return (Integer) settingService.get(PAGE_SIZE, pageSize);
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
settingService.put(PAGE_SIZE, pageSize);
}
public String getSiteSlogan() {
return (String) settingService.get(SITE_SLOGAN, siteSlogan);
}
public void setSiteSlogan(String siteSlogan) {
this.siteSlogan = siteSlogan;
settingService.put(SITE_SLOGAN, siteSlogan);
}
public String getStoragePath() {
return (String) settingService.get(STORAGE_PATH, storagePath);
}
public void setStoragePath(String storagePath) {
this.storagePath = storagePath;
settingService.put(STORAGE_PATH, storagePath);
}
public String getMainUri() {
return (String) settingService.get(MAIN_URI, mainUri);
}
public void setMainUri(String mainUri) {
this.mainUri = mainUri;
settingService.put(MAIN_URI, mainUri);
}
public List<String> getOgLocales() {
ArrayList<String> ogLocales = new ArrayList<>();
ogLocales.add("en_EN");
ogLocales.add("ru_RU");
return ogLocales;
}
public List<String> getOgTypes() {
ArrayList<String> ogTypes = new ArrayList<>();
ogTypes.add("article");
return ogTypes;
}
}

View File

@ -0,0 +1,71 @@
package ru.bvn13.voidforum.services;
import ru.bvn13.voidforum.models.Setting;
import ru.bvn13.voidforum.repositories.SettingRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.io.Serializable;
/**
* bvn13 <mail4bvn@gmail.com>
*/
@Service
public class CacheSettingService implements SettingService {
private SettingRepository settingRepository;
private static final String CACHE_NAME = "cache.settings";
@Autowired
public CacheSettingService(SettingRepository settingRepository) {
this.settingRepository = settingRepository;
}
private static final Logger logger = LoggerFactory.getLogger(SettingService.class);
@Override
public Serializable get(String key) {
Setting setting = settingRepository.findByKey(key);
Serializable value = null;
try {
value = setting == null ? null : setting.getValue();
} catch (Exception ex) {
logger.info("Cannot deserialize setting value with key = " + key);
}
logger.info("Get setting " + key + " from database. Value = " + value);
return value;
}
@Override
@Cacheable(value = CACHE_NAME, key = "#key")
public Serializable get(String key, Serializable defaultValue) {
Serializable value = get(key);
return value == null ? defaultValue : value;
}
@Override
@CacheEvict(value = CACHE_NAME, key = "#key")
public void put(String key, Serializable value) {
logger.info("Update setting " + key + " to database. Value = " + value);
Setting setting = settingRepository.findByKey(key);
if (setting == null) {
setting = new Setting();
setting.setKey(key);
}
try {
setting.setValue(value);
settingRepository.save(setting);
} catch (Exception ex) {
logger.info("Cannot save setting value with type: " + value.getClass() + ". key = " + key);
}
}
}

View File

@ -0,0 +1,88 @@
package ru.bvn13.voidforum.services;
import com.domingosuarez.boot.autoconfigure.jade4j.JadeHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ru.bvn13.voidforum.models.Comment;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.models.support.CommentFormat;
import ru.bvn13.voidforum.repositories.CommentRepository;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
/**
* Created by bvn13 on 09.12.2017.
*/
@JadeHelper("commentService")
//@Service
public class CommentService {
@Autowired
private UserService userService;
@Autowired
private CommentRepository commentRepository;
private static Comparator<Comment> commentComparator = new Comparator<Comment>() {
@Override
public int compare(Comment o1, Comment o2) {
return o1.getId().compareTo(o2.getId());
}
};
public List<Comment> getCommentsForPost(Post post) {
List<Comment> availableComments = null;
//User user = userService.currentUser();
//if (userService.hasPrivilege(user, PrivilegeService.PRIVILEGE_WRITE)) {
availableComments = commentRepository.findAllByPostOrderById(post);
//} else {
// availableComments = commentRepository.findAllByPostAndDeletedMarkOrderById(post, false);
//}
return availableComments;
}
public List<Comment> filterListByParentComment(List<Comment> comments, Comment parent) {
List<Comment> children = new ArrayList<>();
comments.forEach(c -> {
if (c.getParentComment().getId().equals(parent.getId())) {
children.add(c);
}
});
Collections.sort(children, CommentService.commentComparator);
return children;
}
public Integer childrenCount(List<Comment> comments, Comment parent) {
AtomicReference<Integer> count = new AtomicReference<>();
comments.forEach(c -> {
if (c.getParentComment().getId().equals(parent.getId())) {
Integer old = count.get();
count.set(old+1);
}
});
return count.get();
}
public List<CommentFormat> getAvailableCommentFormats() {
List<CommentFormat> formats = new ArrayList<>();
User user = userService.currentUser();
for (CommentFormat f : CommentFormat.values()) {
if (f.equals(CommentFormat.HTML)) {
if (userService.hasPrivilege(user, PrivilegeService.PRIVILEGE_WRITE_HTML)) {
formats.add(f);
}
} else {
formats.add(f);
}
}
return formats;
}
}

View File

@ -0,0 +1,116 @@
package ru.bvn13.voidforum.services;
import ru.bvn13.voidforum.error.NotFoundException;
import ru.bvn13.voidforum.models.StoredFile;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.models.support.HttpContentTypeSerializer;
import ru.bvn13.voidforum.repositories.StoredFileRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@Service
public class FileStorageService {
public static final Logger logger = LoggerFactory.getLogger(FileStorageService.class);
private AppSetting appSetting;
private StoredFileRepository repository;
@Autowired
private UserService userService;
@Autowired
public FileStorageService(StoredFileRepository repository, AppSetting appSetting) {
this.repository = repository;
this.appSetting = appSetting;
logger.debug("== UPLOAD PATH == > "+appSetting.getStoragePath());
}
public StoredFile getFileById(Long id) {
return repository.findById(id);
}
public StoredFile getFileByName(String fileName) {
StoredFile file = null;
file = repository.findByName(fileName);
if (file == null) {
if (fileName.matches("\\d+")) {
file = this.getFileById(Long.valueOf(fileName));
}
}
if (file == null) {
throw new NotFoundException("File " + fileName + " is not found");
}
return file;
}
public void storeFile(User user, String filename, byte[] content) throws IOException {
File storage = new File(appSetting.getStoragePath());
if (!storage.exists()) {
storage.mkdirs();
}
String separator = "";
if (!appSetting.getStoragePath().endsWith("/")) {
separator = "/";
}
String userFolder = appSetting.getStoragePath() + separator + user.getId() + "/";
File userStorage = new File(userFolder);
if (!userStorage.exists()) {
userStorage.mkdirs();
}
String fullname = userFolder + filename;
Path path = Paths.get(fullname);
Files.write(path, content);
File file = new File(fullname);
StoredFile storedFile = new StoredFile();
storedFile.setPath(path.toAbsolutePath().toString());
storedFile.setUser(this.userService.currentUser());
storedFile.setTitle(filename);
storedFile.setName(filename);
storedFile.setSize(file.length());
this.repository.saveAndFlush(storedFile);
}
public byte[] getFileContentById(Long fileId) throws IOException {
StoredFile storedFile = this.repository.findById(fileId);
Path path = Paths.get(storedFile.getPath());
return Files.readAllBytes(path);
}
public byte[] getFileContent(String fullname) throws IOException {
Path path = Paths.get(fullname);
return Files.readAllBytes(path);
}
public void deleteFileById(Long fileId) throws IOException {
StoredFile storedFile = this.repository.findById(fileId);
Path path = Paths.get(storedFile.getPath());
// first delete info, second delete file
// because file might be deleted already
this.repository.delete(storedFile);
Files.delete(path);
}
public String getContentType(String fileName) {
return HttpContentTypeSerializer.getContentType(fileName);
}
}

View File

@ -0,0 +1,68 @@
package ru.bvn13.voidforum.services;
import ru.bvn13.voidforum.models.Like;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.repositories.LikeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LikeService {
@Autowired
private LikeRepository likeRepository;
@Autowired
private UserService userService;
public Integer getTotalLikesByPost(Post post) {
return this.likeRepository.getTotalLikesByPost(post);
}
public Integer getTotalLikesByUserAndPost(User user, Post post) {
return this.likeRepository.getTotalLikesByUserAndPost(user, post);
}
public void likePost(Post post, String clientIp) {
User user = this.userService.currentUser();
Integer currentSympathy = 0;
if (user != null) {
currentSympathy = this.likeRepository.getTotalLikesByUserAndPost(user, post);
} else {
currentSympathy = this.likeRepository.getTotalLikesByClientIpAndPost(clientIp, post);
}
if (currentSympathy == null) currentSympathy = 0;
Integer sympathyDelta = -currentSympathy + 1;
this.saveSympathy(post, user, clientIp, sympathyDelta);
}
public void dislikePost(Post post, String clientIp) {
User user = this.userService.currentUser();
Integer currentSympathy = 0;
if (user != null) {
currentSympathy = this.likeRepository.getTotalLikesByUserAndPost(user, post);
} else {
currentSympathy = this.likeRepository.getTotalLikesByClientIpAndPost(clientIp, post);
}
if (currentSympathy == null) currentSympathy = 0;
Integer sympathyDelta = -currentSympathy - 1;
this.saveSympathy(post, user, clientIp, sympathyDelta);
}
private void saveSympathy(Post post, User user, String clientIp, Integer sympathy) {
Like like = new Like();
like.setClientIp(clientIp);
like.setPost(post);
like.setUser(user);
like.setIsAdmin(user != null ? userService.hasPrivilege(user, PrivilegeService.PRIVILEGE_ADMIN) : false);
like.setSympathy(sympathy);
this.likeRepository.save(like);
}
}

View File

@ -0,0 +1,359 @@
package ru.bvn13.voidforum.services;
import ru.bvn13.voidforum.Constants;
import ru.bvn13.voidforum.error.NotFoundException;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.SeoPostData;
import ru.bvn13.voidforum.models.Tag;
import ru.bvn13.voidforum.models.support.PostFormat;
import ru.bvn13.voidforum.models.support.PostStatus;
import ru.bvn13.voidforum.models.support.PostType;
import ru.bvn13.voidforum.repositories.PostRepository;
import ru.bvn13.voidforum.repositories.SeoPostDataRepository;
import ru.bvn13.voidforum.support.web.MarkdownService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* bvn13 <mail4bvn@gmail.com>.
*/
@Service
public class PostService {
@Autowired
private PostRepository postRepository;
@Autowired
private TagService tagService;
@Autowired
private UserService userService;
@Autowired
private MarkdownService markdownService;
@Autowired
private LikeService likeService;
@Autowired
private VisitService visitService;
@Autowired
private SeoPostDataRepository seoPostDataRepository;
public static final String CACHE_NAME = "cache.post";
public static final String CACHE_NAME_ARCHIVE = CACHE_NAME + ".archive";
public static final String CACHE_NAME_PAGE = CACHE_NAME + ".page";
public static final String CACHE_NAME_TAGS = CACHE_NAME + ".tag";
public static final String CACHE_NAME_SEO_KEYWORDS = CACHE_NAME + ".seoKeyword";
public static final String CACHE_NAME_COUNTS = CACHE_NAME + ".counts_tags";
private static final Logger logger = LoggerFactory.getLogger(PostService.class);
@Cacheable(CACHE_NAME)
public Post getPost(Long postId) {
logger.debug("Get post " + postId);
Post post = postRepository.findOne(postId);
if (post == null) {
throw new NotFoundException("Post with id " + postId + " is not found.");
}
return post;
}
@Cacheable(CACHE_NAME)
public Post getPublishedPost(Long postId) {
logger.debug("Get published post " + postId);
Post post = this.postRepository.findByIdAndPostStatus(postId, PostStatus.PUBLISHED);
if (post == null) {
throw new NotFoundException("Post with id " + postId + " is not found.");
}
return post;
}
@Cacheable(CACHE_NAME)
public Post getPublishedPostByPermalink(String permalink) {
logger.debug("Get post with permalink " + permalink);
Post post = postRepository.findByPermalinkAndPostStatus(permalink, PostStatus.PUBLISHED);
if (post == null) {
throw new NotFoundException("Post with permalink '" + permalink + "' is not found.");
}
return post;
}
@Caching(evict = {
@CacheEvict(value = CACHE_NAME_ARCHIVE, allEntries = true),
@CacheEvict(value = CACHE_NAME_PAGE, allEntries = true),
@CacheEvict(value = CACHE_NAME_COUNTS, allEntries = true)
})
public Post createPost(Post post) {
return this.savePost(post);
}
@Caching(evict = {
@CacheEvict(value = CACHE_NAME, key = "#post.id"),
@CacheEvict(value = CACHE_NAME, key = "#post.permalink", condition = "#post.permalink != null"),
@CacheEvict(value = CACHE_NAME_TAGS, key = "#post.id.toString().concat('-tags')"),
@CacheEvict(value = CACHE_NAME_SEO_KEYWORDS, key = "#post.id.toString().concat('-seoKeywords')"),
@CacheEvict(value = CACHE_NAME_ARCHIVE, allEntries = true),
@CacheEvict(value = CACHE_NAME_PAGE, allEntries = true),
@CacheEvict(value = CACHE_NAME_COUNTS, allEntries = true)
})
public Post updatePost(Post post) {
return this.savePost(post);
}
private Post savePost(Post post) {
if (post.getPostFormat() == PostFormat.MARKDOWN) {
post.setRenderedContent(String.format("<div class=\"markdown-post\">%s</div>", markdownService.renderToHtml(post.getContent())));
} else {
post.setRenderedContent(String.format("<div class=\"html-post\">%s</div>", post.getContent()));
}
this.saveSeoData(post);
return postRepository.save(post);
}
@Caching(evict = {
@CacheEvict(value = CACHE_NAME, key = "#post.id"),
@CacheEvict(value = CACHE_NAME, key = "#post.permalink", condition = "#post.permalink != null"),
@CacheEvict(value = CACHE_NAME_TAGS, key = "#post.id.toString().concat('-tags')"),
@CacheEvict(value = CACHE_NAME_SEO_KEYWORDS, key = "#post.id.toString().concat('-seoKeywords')"),
@CacheEvict(value = CACHE_NAME_ARCHIVE, allEntries = true),
@CacheEvict(value = CACHE_NAME_PAGE, allEntries = true),
@CacheEvict(value = CACHE_NAME_COUNTS, allEntries = true)
})
public void deletePost(Post post) {
post.setDeletedMark(!post.getDeletedMark());
postRepository.save(post);
}
public void censorePost(Post post) {
post.setCensored(!post.getCensored());
postRepository.save(post);
}
@Cacheable(value = CACHE_NAME_ARCHIVE, key = "#root.method.name")
public List<Post> getArchivePosts() {
logger.debug("Get all archive posts from database.");
Iterable<Post> posts = postRepository.findAllByPostTypeAndPostStatus(
PostType.POST,
PostStatus.PUBLISHED,
new PageRequest(0, Integer.MAX_VALUE, Sort.Direction.DESC, "createdAt"));
List<Post> cachedPosts = new ArrayList<>();
posts.forEach(post -> cachedPosts.add(extractPostMeta(post)));
return cachedPosts;
}
@Cacheable(value = CACHE_NAME_TAGS, key = "#post.id.toString().concat('-tags')")
public List<Tag> getPostTags(Post post) {
logger.debug("Get tags of post " + post.getId());
List<Tag> tags = new ArrayList<>();
// Load the post first. If not, when the post is cached before while the tags not,
// then the LAZY loading of post tags will cause an initialization error because
// of not hibernate connection session
postRepository.findOne(post.getId()).getTags().forEach(tags::add);
return tags;
}
@Cacheable(value = CACHE_NAME_SEO_KEYWORDS, key = "#post.id.toString().concat('-seoKeywords')")
public String getSeoKeywordsAsString(Post post) {
logger.debug("Get seoKeywordsAsString of post " + post.getId());
return post.getSeoKeywords();
}
private Post extractPostMeta(Post post) {
Post archivePost = new Post();
archivePost.setId(post.getId());
archivePost.setTitle(post.getTitle());
archivePost.setPermalink(post.getPermalink());
archivePost.setCreatedAt(post.getCreatedAt());
archivePost.setSympathyCount(this.likeService.getTotalLikesByPost(post));
archivePost.setVisitsCount(this.visitService.getUniqueVisitsCount(post));
return archivePost;
}
@Cacheable(value = CACHE_NAME_PAGE, key = "T(java.lang.String).valueOf(#page).concat('-').concat(#pageSize)")
public Page<Post> getAllPublishedPostsByPage(int page, int pageSize) {
logger.debug("Get posts by page " + page);
Page<Post> posts = postRepository.findAllByPostTypeAndPostStatus(
PostType.POST,
PostStatus.PUBLISHED,
new PageRequest(page, pageSize, Sort.Direction.DESC, "createdAt"));
posts.forEach(p -> {
p.setSympathyCount(this.likeService.getTotalLikesByPost(p));
p.setVisitsCount(this.visitService.getUniqueVisitsCount(p));
});
return posts;
}
@Cacheable(value = CACHE_NAME_PAGE, key = "T(java.lang.String).valueOf(#page).concat('-').concat(#pageSize)")
public Page<Post> getAllPublishedNotDeletedPostsByPage(int page, int pageSize) {
logger.debug("Get not deleted posts by page " + page);
Page<Post> posts = postRepository.findAllByPostTypeAndPostStatusAndDeletedMark(
PostType.POST,
PostStatus.PUBLISHED,
false,
new PageRequest(page, pageSize, Sort.Direction.DESC, "createdAt"));
posts.forEach(p -> {
p.setSympathyCount(this.likeService.getTotalLikesByPost(p));
p.setVisitsCount(this.visitService.getUniqueVisitsCount(p));
});
return posts;
}
@Cacheable(value = CACHE_NAME_PAGE, key = "T(java.lang.String).valueOf(#page).concat('-').concat(#pageSize)")
public Page<Post> getAllPublishedNotCensoredNotDeletedPostsByPage(int page, int pageSize) {
logger.debug("Get not censored posts by page " + page);
Page<Post> posts = postRepository.findAllByPostTypeAndPostStatusAndDeletedMarkAndCensored(
PostType.POST,
PostStatus.PUBLISHED,
false, false,
new PageRequest(page, pageSize, Sort.Direction.DESC, "createdAt"));
posts.forEach(p -> {
p.setSympathyCount(this.likeService.getTotalLikesByPost(p));
p.setVisitsCount(this.visitService.getUniqueVisitsCount(p));
});
return posts;
}
public List<Post> getAllPublishedPosts() {
logger.debug("Get all published posts");
return this.postRepository.findAllByPostTypeAndPostStatus(PostType.POST, PostStatus.PUBLISHED);
}
// public Post createAboutPage() {
// logger.debug("Create default about page");
//
// Post post = new Post();
// post.setTitle(Constants.ABOUT_PAGE_PERMALINK);
// post.setContent(Constants.ABOUT_PAGE_PERMALINK.toLowerCase());
// post.setPermalink(Constants.ABOUT_PAGE_PERMALINK);
// post.setUser(userService.getSuperUser());
// post.setPostFormat(PostFormat.MARKDOWN);
//
// return createPost(post);
// }
// public Post createProjectsPage() {
// logger.debug("Create default projects page");
//
// Post post = new Post();
// post.setTitle(Constants.PROJECTS_PAGE_PERMALINK);
// post.setContent(Constants.PROJECTS_PAGE_PERMALINK.toLowerCase());
// post.setPermalink(Constants.PROJECTS_PAGE_PERMALINK);
// post.setUser(userService.getSuperUser());
// post.setPostFormat(PostFormat.MARKDOWN);
//
// return createPost(post);
// }
public Set<Tag> parseTagNames(String tagNames) {
Set<Tag> tags = new HashSet<>();
if (tagNames != null && !tagNames.isEmpty()) {
tagNames = tagNames.toLowerCase();
String[] names = tagNames.split("\\s*,\\s*");
for (String name : names) {
tags.add(tagService.findOrCreateByName(name));
}
}
return tags;
}
public String getTagNames(Set<Tag> tags) {
if (tags == null || tags.isEmpty())
return "";
StringBuilder names = new StringBuilder();
tags.forEach(tag -> names.append(tag.getName()).append(","));
names.deleteCharAt(names.length() - 1);
return names.toString();
}
// cache or not?
public Page<Post> findPostsByTag(String tagName, int page, int pageSize) {
return postRepository.findByTag(tagName, new PageRequest(page, pageSize, Sort.Direction.DESC, "createdAt"));
}
@Cacheable(value = CACHE_NAME_COUNTS, key = "#root.method.name")
public List<Object[]> countPostsByTags() {
logger.debug("Count posts group by tags.");
return postRepository.countPostsByTags(PostStatus.PUBLISHED);
}
public Post findPostByPermalink(String permalink) {
Post post = null;
try{
post = this.getPublishedPostByPermalink(permalink);
} catch (NotFoundException ex){
if (permalink.matches("\\d+")) {
if (this.userService.isCurrentUserAdmin()) {
post = this.getPost(Long.valueOf(permalink));
} else {
post = this.getPublishedPost(Long.valueOf(permalink));
}
}/* else if (permalink.toLowerCase().trim().equals(Constants.PROJECTS_PAGE_PERMALINK)) {
post = this.createProjectsPage();
}*/
}
if (post == null) {
throw new NotFoundException("Post with permalink " + permalink + " is not found");
}
return post;
}
private void saveSeoData(Post post) {
if (post.getSeoData() != null && post.getSeoData().getId() == null) {
SeoPostData data = post.getSeoData();
this.seoPostDataRepository.save(data);
}
}
}

View File

@ -0,0 +1,13 @@
package ru.bvn13.voidforum.services;
import org.springframework.stereotype.Service;
@Service
public class PrivilegeService {
public static final String PRIVILEGE_OWNER = "OWNER_PRIVILEGE";
public static final String PRIVILEGE_ADMIN = "ADMIN_PRIVILEGE";
public static final String PRIVILEGE_READ = "READ_PRIVILEGE";
public static final String PRIVILEGE_WRITE = "WRITE_PRIVILEGE";
public static final String PRIVILEGE_WRITE_HTML = "WRITE_PRIVILEGE_HTML";
public static final String PRIVILEGE_BLOCKED = "BLOCKED_PRIVILEGE";
}

View File

@ -0,0 +1,23 @@
package ru.bvn13.voidforum.services;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
@Service
public class RequestProcessorService {
public String getRealIp(HttpServletRequest request) {
String xRealIp = request.getHeader("X-Real-IP");
if (xRealIp == null || request.getHeader("X-Real-IP").isEmpty()) {
return request.getRemoteAddr();
}
return xRealIp;
}
public String getUserAgent(HttpServletRequest request) {
String userAgent = request.getHeader("User-Agent");
return userAgent;
}
}

View File

@ -0,0 +1,15 @@
package ru.bvn13.voidforum.services;
import org.springframework.stereotype.Service;
@Service
public class RoleService {
public static final String ROLE_OWNER = "ROLE_OWNER";
public static final String ROLE_ADMIN = "ROLE_ADMIN";
public static final String ROLE_ADMIN_WITH_HTML_EDITOR = "ROLE_ADMIN_WITH_HTML_EDITOR";
public static final String ROLE_USER = "ROLE_USER";
public static final String ROLE_USER_WITH_HTML_EDITOR = "ROLE_USER_WITH_HTML_EDITOR";
public static final String ROLE_VISITOR = "ROLE_VISITOR";
public static final String ROLE_BLOCKED = "ROLE_BLOCKED";
}

View File

@ -0,0 +1,10 @@
package ru.bvn13.voidforum.services;
import org.springframework.stereotype.Service;
@Service
public class SeoRobotAgentService {
}

View File

@ -0,0 +1,81 @@
package ru.bvn13.voidforum.services;
import ru.bvn13.voidforum.models.Post;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.List;
@Service
public class SeoService {
public static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
@Autowired
private AppSetting appSetting;
public String createSitemap(List<Post> posts) {
String slash = appSetting.getMainUri().trim().endsWith("/") ? "" : "/";
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
Element root = doc.createElement("urlset");
root.setAttribute("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9");
doc.appendChild(root);
for(Post post : posts) {
Element url = doc.createElement("url");
//loc
Element loc = doc.createElement("loc");
loc.appendChild(doc.createTextNode(String.format("%s%sposts/%s", appSetting.getMainUri().trim(), slash, post.getPermalink() == null || post.getPermalink().isEmpty() ? post.getId() : post.getPermalink())));
url.appendChild(loc);
//lastmod
//yyyy-MM-dd'T'HH:mm:ss
Element lastMod = doc.createElement("lastmod");
lastMod.appendChild(doc.createTextNode(dateFormatter.format(post.getUpdatedAt())));
url.appendChild(lastMod);
//
Element changeFreq = doc.createElement("changefreq");
changeFreq.appendChild(doc.createTextNode("daily"));
url.appendChild(changeFreq);
root.appendChild(url);
}
// write the content into xml file
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StringWriter sw = new StringWriter();
transformer.transform(new DOMSource(doc), new StreamResult(sw));
return sw.toString();
} catch (ParserConfigurationException e) {
e.printStackTrace();
return null;
} catch (TransformerException tfe) {
tfe.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,12 @@
package ru.bvn13.voidforum.services;
import java.io.Serializable;
/**
* bvn13 <mail4bvn@gmail.com>
*/
public interface SettingService {
Serializable get(String key);
Serializable get(String key, Serializable defaultValue);
void put(String key, Serializable value);
}

View File

@ -0,0 +1,63 @@
package ru.bvn13.voidforum.services;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import ru.bvn13.voidforum.models.Tag;
import ru.bvn13.voidforum.repositories.TagRepository;
import java.util.List;
/**
* bvn13 <mail4bvn@gmail.com>.
*/
@Service
public class TagService {
private TagRepository tagRepository;
private static final Logger logger = LoggerFactory.getLogger(PostService.class);
public static final String CACHE_NAME = "cache.tag";
public static final String CACHE_NAME_TAGS = "cache.tag.all";
public static final String CACHE_TYPE = "'_Tag_'";
public static final String CACHE_KEY = CACHE_TYPE + " + #tagName";
public static final String CACHE_TAG_KEY = CACHE_TYPE + " + #tag.name";
@Autowired
public TagService(TagRepository tagRepository){
this.tagRepository = tagRepository;
}
public Tag findOrCreateByName(String name){
Tag tag = tagRepository.findByName(name);
if (tag == null){
tag = tagRepository.save(new Tag(name));
}
return tag;
}
@Cacheable(value = CACHE_NAME, key = CACHE_KEY)
public Tag getTag(String tagName) {
return tagRepository.findByName(tagName);
}
@Caching(evict = {
@CacheEvict(value = CACHE_NAME, key = CACHE_TAG_KEY),
@CacheEvict(value = CACHE_NAME_TAGS, allEntries = true)
})
public void deleteTag(Tag tag){
tagRepository.delete(tag);
}
@Cacheable(value = CACHE_NAME_TAGS, key = "#root.method.name")
public List<Tag> getAllTags(){
return tagRepository.findAll();
}
}

View File

@ -0,0 +1,233 @@
package ru.bvn13.voidforum.services;
import com.domingosuarez.boot.autoconfigure.jade4j.JadeHelper;
import org.hibernate.Hibernate;
import org.hibernate.SessionFactory;
import org.hibernate.hql.internal.ast.util.SessionFactoryHelper;
import ru.bvn13.voidforum.error.EmailExistsException;
import ru.bvn13.voidforum.error.NicknameExistsException;
import ru.bvn13.voidforum.models.Privilege;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.models.Role;
import ru.bvn13.voidforum.models.support.PostFormat;
import ru.bvn13.voidforum.repositories.PrivilegeRepository;
import ru.bvn13.voidforum.repositories.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import ru.bvn13.voidforum.repositories.RoleRepository;
import ru.bvn13.voidforum.utils.DTOUtil;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.transaction.Transactional;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
@JadeHelper("userService")
public class UserService implements UserDetailsService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Autowired
private PrivilegeRepository privilegeRepository;
@Inject
private PasswordEncoder passwordEncoder;
// @Autowired
// private SessionFactory sessionFactory;
@PostConstruct
protected void initialize() {
//getSuperUser();
}
public User createUser(User user){
user.setPassword(passwordEncoder.encode(user.getPassword()));
return userRepository.save(user);
}
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
org.springframework.security.core.userdetails.User springUser = null;
//sessionFactory.openSession();
User user = userRepository.findByEmail(email);
if (user == null) {
springUser = new org.springframework.security.core.userdetails.User(
" ", " ", true, true, true, true,
getAuthorities(Arrays.asList(
roleRepository.findByName(RoleService.ROLE_VISITOR))
)
);
} else {
List<Role> roles = roleRepository.findAllByUser(user);
springUser = new org.springframework.security.core.userdetails.User(
user.getEmail(), user.getPassword(), !user.getDisabled(), true, true,
true, getAuthorities(roles)
);
}
//sessionFactory.close();
return springUser;
}
private Collection<? extends GrantedAuthority> getAuthorities(Collection<Role> roles) {
return getGrantedAuthorities(getPrivileges(roles));
}
public List<String> getPrivileges(Collection<Role> roles) {
List<String> privileges = new ArrayList<>();
List<Privilege> collection = new ArrayList<>();
for (Role role : roles) {
//role.setPrivileges(privilegeRepository.findAllByRole(role));
collection.addAll(privilegeRepository.findAllByRole(role));
}
for (Privilege item : collection) {
privileges.add(item.getName());
}
return privileges;
}
private List<GrantedAuthority> getGrantedAuthorities(List<String> privileges) {
List<GrantedAuthority> authorities = new ArrayList<>();
for (String privilege : privileges) {
authorities.add(new SimpleGrantedAuthority(privilege));
}
return authorities;
}
public User currentUser(){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if(auth == null || auth instanceof AnonymousAuthenticationToken){
return null;
}
String email = ((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername();
return userRepository.findByEmail(email);
}
public Boolean isCurrentUserAdmin() {
User user = this.currentUser();
Boolean isAdmin = user != null ? this.hasPrivilege(user, PrivilegeService.PRIVILEGE_ADMIN) : false;
return isAdmin;
}
public boolean changePassword(User user, String password, String newPassword){
if (password == null || newPassword == null || password.isEmpty() || newPassword.isEmpty())
return false;
logger.info("" + passwordEncoder.matches(password, user.getPassword()));
boolean match = passwordEncoder.matches(password, user.getPassword());
if (!match)
return false;
user.setPassword(passwordEncoder.encode(newPassword));
userRepository.save(user);
logger.info("User @"+user.getEmail() + " changed password.");
return true;
}
public Boolean currentUserHasPrivilege(String privilege) {
User user = this.currentUser();
return this.hasPrivilege(user, privilege);
}
public User registerNewUserAccount(User user) throws EmailExistsException, NicknameExistsException {
if (emailExist(user.getEmail())) {
throw new EmailExistsException("There is an account with that email address: " + user.getEmail());
}
if (nicknameExists(user.getNickname())) {
throw new NicknameExistsException("There is an account with that nickname: " + user.getNickname());
}
user.setPassword(passwordEncoder.encode(user.getPassword()));
user.setDisabled(false);
user.setRoles(Arrays.asList(roleRepository.findByName(RoleService.ROLE_USER)));
return userRepository.save(user);
}
public Boolean emailExist(String email) {
return this.userRepository.findByEmail(email) != null;
}
public Boolean nicknameExists(String nickname) {
return this.userRepository.findByNickname(nickname) != null;
}
public Boolean hasRole(User user, String role) {
if (user == null) {
return false;
}
AtomicReference<Boolean> hasRole = new AtomicReference<>(false);
user.getRoles().forEach(r -> {
if (r.getName().equals(role)) {
hasRole.set(true);
}
});
return hasRole.get();
}
public Boolean hasPrivilege(User user, String privilege) {
if (user == null) {
return false;
}
AtomicReference<Boolean> hasPrivilege = new AtomicReference<>(false);
user.getRoles().forEach(r -> {
r.getPrivileges().forEach(p -> {
if (p.getName().equals(privilege)) {
hasPrivilege.set(true);
}
});
});
return hasPrivilege.get();
}
public List<PostFormat> getAvailablePostFormats(User user) {
List<PostFormat> availableFormats = new ArrayList<>();
for (PostFormat postFormat : PostFormat.values()) {
if (postFormat.equals(PostFormat.HTML)) {
if (hasPrivilege(user, PrivilegeService.PRIVILEGE_WRITE_HTML)) {
availableFormats.add(postFormat);
}
} else {
availableFormats.add(postFormat);
}
}
return availableFormats;
}
}

View File

@ -0,0 +1,103 @@
package ru.bvn13.voidforum.services;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.SeoRobotAgent;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.models.Visit;
import ru.bvn13.voidforum.repositories.SeoRobotAgentRepository;
import ru.bvn13.voidforum.repositories.VisitRepository;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.persistence.EntityManager;
import java.math.BigInteger;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Service
public class VisitService {
@Autowired
private VisitRepository visitRepository;
@Autowired
private SeoRobotAgentRepository seoRobotAgentRepository;
@Autowired
private UserService userService;
@Autowired
private EntityManager entityManager;
public void saveVisit(Post post, String clientIp, String userAgent) {
// if (this.userService.currentUser().isAdmin())
// return;
User user = this.userService.currentUser();
Visit visit = new Visit();
visit.setClientIp(clientIp);
visit.setPost(post);
visit.setUser(user);
visit.setIsAdmin(user != null ? userService.hasPrivilege(user, PrivilegeService.PRIVILEGE_ADMIN) : false);
visit.setUserAgent(userAgent);
this.visitRepository.save(visit);
}
public Long getUniqueVisitsCount(Post post) {
Session session = (Session) this.entityManager.getDelegate();
SQLQuery query = session.createSQLQuery(
"SELECT COUNT(DISTINCT v.clientIp) " +
"FROM visits AS v " +
"LEFT JOIN seo_robots_agents AS ra " +
//"ON LOWER(v.userAgent) LIKE concat('%', LOWER(ra.userAgent), '%') " +
"ON CASE WHEN ra.isregexp = TRUE THEN " +
"LOWER(v.userAgent) SIMILAR TO concat(LOWER(ra.userAgent)) " +
"ELSE " +
"LOWER(v.userAgent) LIKE concat('%', LOWER(ra.userAgent), '%') " +
"END " +
"WHERE v.post_id = :post_id AND v.isAdmin = FALSE " +
"AND ra.id IS NULL ");
query.setLong("post_id", post.getId());
List<Object> result = query.list();
if (result.size() > 0L) {
return ((BigInteger)result.get(0)).longValue();
}
return 0L;
}
public Long getUniqueVisitsCount_old(Post post) {
//return this.visitRepository.getUniquePostVisitsCount(post);
// exclude queries from robots if matches by UserAgent
List<SeoRobotAgent> robotsAgents = this.seoRobotAgentRepository.findAll();
final Long[] count = {0L};
this.visitRepository.getVisitsByPostAndIsAdminIsFalse(post).forEach(vr -> {
Object[] v = (Object[]) vr;
if (robotsAgents.size() == 0 || v[1] == null) {
count[0]++;
} else {
robotsAgents.forEach(ra -> {
Pattern p = Pattern.compile(".*("+ra.getUserAgent()+").*", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher((String) v[1]);
if (!m.matches()) {
count[0]++;
}
});
}
});
return count[0];
}
}

View File

@ -0,0 +1,133 @@
package ru.bvn13.voidforum.support;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import ru.bvn13.voidforum.Constants;
import ru.bvn13.voidforum.models.Privilege;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.models.Role;
import ru.bvn13.voidforum.repositories.PrivilegeRepository;
import ru.bvn13.voidforum.repositories.UserRepository;
import ru.bvn13.voidforum.repositories.RoleRepository;
import ru.bvn13.voidforum.services.PrivilegeService;
import ru.bvn13.voidforum.services.RoleService;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@Component
public class InitialDataLoader implements ApplicationListener<ContextRefreshedEvent> {
boolean alreadySetup = false;
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Autowired
private PrivilegeRepository privilegeRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
@Transactional
public void onApplicationEvent(ContextRefreshedEvent event) {
if (alreadySetup)
return;
Privilege ownerPrivilege = createPrivilegeIfNotFound(PrivilegeService.PRIVILEGE_OWNER);
Privilege adminPrivilege = createPrivilegeIfNotFound(PrivilegeService.PRIVILEGE_ADMIN);
Privilege readPrivilege = createPrivilegeIfNotFound(PrivilegeService.PRIVILEGE_READ);
Privilege writePrivilege = createPrivilegeIfNotFound(PrivilegeService.PRIVILEGE_WRITE);
Privilege writeHtmlPrivilege = createPrivilegeIfNotFound(PrivilegeService.PRIVILEGE_WRITE_HTML);
Privilege blockedPrivilege = createPrivilegeIfNotFound(PrivilegeService.PRIVILEGE_BLOCKED);
Role roleOwner = createRoleIfNotFound(RoleService.ROLE_OWNER, Arrays.asList(ownerPrivilege, adminPrivilege, writePrivilege, writeHtmlPrivilege, readPrivilege));
createRoleIfNotFound(RoleService.ROLE_ADMIN, Arrays.asList(adminPrivilege, readPrivilege, writePrivilege));
createRoleIfNotFound(RoleService.ROLE_ADMIN_WITH_HTML_EDITOR, Arrays.asList(adminPrivilege, readPrivilege, writePrivilege, writeHtmlPrivilege));
createRoleIfNotFound(RoleService.ROLE_USER, Arrays.asList(readPrivilege, writePrivilege));
createRoleIfNotFound(RoleService.ROLE_USER_WITH_HTML_EDITOR, Arrays.asList(readPrivilege, writePrivilege, writeHtmlPrivilege));
createRoleIfNotFound(RoleService.ROLE_VISITOR, Arrays.asList(readPrivilege));
createRoleIfNotFound(RoleService.ROLE_BLOCKED, Arrays.asList(readPrivilege, blockedPrivilege));
if (userRepository.findAllByOneRoleByNameOrderById(RoleService.ROLE_OWNER).size() == 0) {
User user = new User();
user.setNickname(Constants.DEFAULT_ADMIN_NICKNAME);
user.setEmail(Constants.DEFAULT_ADMIN_EMAIL);
user.setPassword(passwordEncoder.encode(Constants.DEFAULT_ADMIN_PASSWORD));
user.setRoles(Arrays.asList(roleOwner));
user.setDisabled(false);
userRepository.save(user);
}
alreadySetup = true;
}
@Transactional
public Privilege createPrivilegeIfNotFound(String name) {
Privilege privilege = privilegeRepository.findByName(name);
if (privilege == null) {
privilege = new Privilege(name);
privilegeRepository.save(privilege);
}
return privilege;
}
@Transactional
public Role createRoleIfNotFound(String name, Collection<Privilege> privileges) {
Role role = roleRepository.findByName(name);
if (role == null) {
role = new Role(name);
role.setPrivileges(privileges);
} else {
// add new privileges
for (Privilege privilege : privileges) {
boolean found = false;
for (Privilege p : role.getPrivileges()) {
if (p.getName().equals(privilege.getName())) {
found = true;
break;
}
}
if (!found) {
role.getPrivileges().add(privilege);
}
}
// remove unused privileges
List<Privilege> privilegeListForRemoving = new ArrayList<>();
for (Privilege p : role.getPrivileges()) {
boolean found = false;
for (Privilege privilege : privileges) {
if (p.getName().equals(privilege.getName())) {
found = true;
break;
}
}
if (!found) {
privilegeListForRemoving.add(p);
}
}
for (Privilege privilege : privilegeListForRemoving) {
role.getPrivileges().remove(privilege);
}
}
roleRepository.save(role);
return role;
}
}

View File

@ -0,0 +1,48 @@
package ru.bvn13.voidforum.support.web;
import com.vladsch.flexmark.ast.Node;
import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension;
import com.vladsch.flexmark.ext.tables.TablesExtension;
import com.vladsch.flexmark.ext.youtube.embedded.YouTubeLinkExtension;
import com.vladsch.flexmark.html.HtmlRenderer;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.options.MutableDataSet;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.util.Arrays;
@Service
@Qualifier("flexmark")
public class FlexmarkMarkdownService implements MarkdownService, SyntaxHighlightService {
@Override
public String renderToHtml(String content) {
MutableDataSet options = new MutableDataSet();
// uncomment to set optional extensions
options.set(Parser.EXTENSIONS, Arrays.asList(
TablesExtension.create(),
StrikethroughExtension.create(),
//YoutubeLinkTransformer.YouTubeLinkExtension.create(),
YouTubeLinkExtension.create()
));
// uncomment to convert soft-breaks to hard breaks
//options.set(HtmlRenderer.SOFT_BREAK, "<br />\n");
Parser parser = Parser.builder(options).build();
HtmlRenderer renderer = HtmlRenderer.builder(options).build();
// You can re-use parser and renderer instances
Node document = parser.parse(content);
String html = renderer.render(document); // "<p>This is <em>Sparta</em></p>\n"
return html;
}
@Override
public String highlight(String content) {
return content;
}
}

View File

@ -0,0 +1,8 @@
package ru.bvn13.voidforum.support.web;
/**
* @author bvn13 <mail4bvn@gmail.com>
*/
public interface MarkdownService {
public String renderToHtml(String content);
}

View File

@ -0,0 +1,46 @@
package ru.bvn13.voidforum.support.web;
/**
* A message to be displayed in web context. Depending on the type, different style will be applied.
*/
public class Message implements java.io.Serializable {
/**
* Name of the flash attribute.
*/
public static final String MESSAGE_ATTRIBUTE = "message";
/**
* The type of the message to be displayed. The type is used to show message in a different style.
*/
public static enum Type {
DANGER, WARNING, INFO, SUCCESS;
}
private final String message;
private final Type type;
private final Object[] args;
public Message(String message, Type type) {
this.message = message;
this.type = type;
this.args = null;
}
public Message(String message, Type type, Object... args) {
this.message = message;
this.type = type;
this.args = args;
}
public String getMessage() {
return message;
}
public Type getType() {
return type;
}
public Object[] getArgs() {
return args;
}
}

View File

@ -0,0 +1,53 @@
package ru.bvn13.voidforum.support.web;
import org.springframework.ui.Model;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import static ru.bvn13.voidforum.support.web.Message.MESSAGE_ATTRIBUTE;
public final class MessageHelper {
private MessageHelper() {
}
public static void addSuccessAttribute(RedirectAttributes ra, String message, Object... args) {
addAttribute(ra, message, Message.Type.SUCCESS, args);
}
public static void addErrorAttribute(RedirectAttributes ra, String message, Object... args) {
addAttribute(ra, message, Message.Type.DANGER, args);
}
public static void addInfoAttribute(RedirectAttributes ra, String message, Object... args) {
addAttribute(ra, message, Message.Type.INFO, args);
}
public static void addWarningAttribute(RedirectAttributes ra, String message, Object... args) {
addAttribute(ra, message, Message.Type.WARNING, args);
}
private static void addAttribute(RedirectAttributes ra, String message, Message.Type type, Object... args) {
ra.addFlashAttribute(MESSAGE_ATTRIBUTE, new Message(message, type, args));
}
public static void addSuccessAttribute(Model model, String message, Object... args) {
addAttribute(model, message, Message.Type.SUCCESS, args);
}
public static void addErrorAttribute(Model model, String message, Object... args) {
addAttribute(model, message, Message.Type.DANGER, args);
}
public static void addInfoAttribute(Model model, String message, Object... args) {
addAttribute(model, message, Message.Type.INFO, args);
}
public static void addWarningAttribute(Model model, String message, Object... args) {
addAttribute(model, message, Message.Type.WARNING, args);
}
private static void addAttribute(Model model, String message, Message.Type type, Object... args) {
model.addAttribute(MESSAGE_ATTRIBUTE, new Message(message, type, args));
}
}

View File

@ -0,0 +1,8 @@
package ru.bvn13.voidforum.support.web;
/**
* @author bvn13 <mail4bvn@gmail.com>
*/
public interface SyntaxHighlightService {
public String highlight(String content);
}

View File

@ -0,0 +1,133 @@
package ru.bvn13.voidforum.support.web;
import com.domingosuarez.boot.autoconfigure.jade4j.JadeHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.support.WebError;
import ru.bvn13.voidforum.services.AppSetting;
import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
/**
* @author bvn13 <mail4bvn@gmail.com>
*/
@Service
@JadeHelper("viewHelper")
public class ViewHelperVF {
private static Logger logger = LoggerFactory.getLogger(ViewHelperVF.class);
private static DateFormatSymbols ruDateFormatSymbolsFull = new DateFormatSymbols(){
@Override
public String[] getMonths() {
return new String[]{"январь", "февраль", "март", "апрель", "май", "июнь", "июль", "август", "сентябрь", "октябрь", "ноябрь", "декабрь"};
}
};
private static DateFormatSymbols ruDateFormatSymbolsShort = new DateFormatSymbols(){
@Override
public String[] getMonths() {
return new String[]{"янв", "фев", "мар", "апр", "май", "июн", "июл", "авг", "сен", "окт", "ноя", "дек"};
}
};
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MMMM dd, yyyy", ViewHelperVF.ruDateFormatSymbolsFull);
private static final SimpleDateFormat DATE_FORMAT_MONTH_DAY = new SimpleDateFormat("MMM dd", ViewHelperVF.ruDateFormatSymbolsShort);
private AppSetting appSetting;
private String applicationEnv;
@Autowired
public ViewHelperVF(AppSetting appSetting){
this.appSetting = appSetting;
}
private long startTime;
public long getResponseTime(){
return System.currentTimeMillis() - startTime;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public String getFormattedDate(Date date){
return date == null ? "" : DATE_FORMAT.format(date);
}
public String getMonthAndDay(Date date){
return date == null ? "" : DATE_FORMAT_MONTH_DAY.format(date);
}
public String metaTitle(String title){
return title + " · " + appSetting.getSiteName();
}
public String getApplicationEnv() {
return applicationEnv;
}
public Boolean isProductionMode() {
return this.applicationEnv.equalsIgnoreCase("production");
}
public void setApplicationEnv(String applicationEnv) {
this.applicationEnv = applicationEnv;
}
public String formatNumberByThousands(Long number) {
if (number == null)
return "0";
double thousands = number / 1000;
double millions = thousands / 1000;
if (millions > 0d) {
return String.format("%.3f", millions);
} else if (thousands > 0d) {
return String.format("%.3fK", thousands);
} else {
return String.format("%d", number);
}
}
public String formatNumberByThousands(Integer number) {
return this.formatNumberByThousands((long) number);
}
public String errorFormat(Map<String, WebError> errors, String field) {
if (errors != null && errors.containsKey(field)) {
return errors.get(field).getErrorMessage();
} else {
return "";
}
}
public String getPostUrl(Post post) {
return String.format("%s/posts/%s",
this.appSetting.getMainUri().endsWith("/") ? this.appSetting.getMainUri().substring(0, this.appSetting.getMainUri().length()-1) : this.appSetting.getMainUri(),
post.getPermalink().isEmpty() ? post.getId() : post.getPermalink()
);
}
public String getAbsoluteUrl(String url) {
if (url.isEmpty()) {
return "";
}
return String.format("%s/%s",
this.appSetting.getMainUri().endsWith("/") ? this.appSetting.getMainUri().substring(0, this.appSetting.getMainUri().length()-1) : this.appSetting.getMainUri(),
url.startsWith("/") ? url.substring(1) : url
);
}
}

View File

@ -0,0 +1,55 @@
package ru.bvn13.voidforum.utils;
import org.hibernate.Hibernate;
import org.hibernate.proxy.HibernateProxy;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import java.util.ArrayList;
import java.util.List;
/**
* @author bvn13 <mail4bvn@gmail.com>
*/
public class DTOUtil {
private static ModelMapper MAPPER = null;
private static ModelMapper getMapper(){
if(MAPPER == null){
MAPPER = new ModelMapper();
MAPPER.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
}
return MAPPER;
}
public static <S, T> T map(S source, Class<T> targetClass) {
return getMapper().map(source, targetClass);
}
public static <S, T> void mapTo(S source, T dist) {
getMapper().map(source, dist);
}
public static <S, T> List<T> mapList(List<S> source, Class<T> targetClass) {
List<T> list = new ArrayList<>();
for (S s : source) {
list.add(getMapper().map(s, targetClass));
}
return list;
}
public static <T> T initializeAndUnproxy(T entity) {
if (entity == null) {
throw new
NullPointerException("Entity passed for initialization is null");
}
Hibernate.initialize(entity);
if (entity instanceof HibernateProxy) {
entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer().getImplementation();
}
return entity;
}
}

View File

@ -0,0 +1,46 @@
server:
port: 9000
contextPath:
spring:
profiles:
active: dev
thymeleaf:
#mode: html5
#suffix: .html
cache: false
jade4j:
caching: false
dataSource:
driverClassName: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/voidforum
username: voidforum
password: vfpass
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
hbm2ddl.auto: update
show_sql: false
redis:
embedded : false
host: localhost
port: 6379
default_expire_time: 86400
session:
store-type: redis
jpa:
properties:
hibernate:
current_session_context_class: org.springframework.orm.hibernate5.SpringSessionContext
http:
multipart:
max-file-size: 100Mb
max-request-size: 100Mb

View File

@ -0,0 +1,7 @@
view.index.title=Home page
signup.success=Congratulations {0}! You have successfully signed up.
signin.already=You have already signed in.
# Validation messages
notBlank.message = The value may not be empty!
email.message = The value must be a valid email!

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