reddit improvements

This commit is contained in:
DOHA 2015-05-09 22:30:57 +02:00
parent 57bfaaed1c
commit 60147a272f
24 changed files with 292 additions and 229 deletions

View File

@ -0,0 +1 @@
web: java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT target/*.war

View File

@ -106,6 +106,26 @@
<version>${httpcore.version}</version> <version>${httpcore.version}</version>
</dependency> </dependency>
<!-- thymeleaf -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity3</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<!-- persistence --> <!-- persistence -->
<dependency> <dependency>
@ -140,6 +160,14 @@
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<!-- h2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.187</version>
</dependency>
<!-- web --> <!-- web -->
<dependency> <dependency>
@ -329,6 +357,29 @@
</configuration> </configuration>
</plugin> </plugin>
<!-- webapp runner -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>copy</goal></goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.github.jsimone</groupId>
<artifactId>webapp-runner</artifactId>
<version>7.0.57.2</version>
<destFileName>webapp-runner.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@ -34,7 +34,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
.httpBasic().authenticationEntryPoint(oauth2AuthenticationEntryPoint()) .httpBasic().authenticationEntryPoint(oauth2AuthenticationEntryPoint())
.and() .and()
.logout() .logout()
.deleteCookies("JSESSIONID","CustomRememberMe") .deleteCookies("JSESSIONID")
.logoutUrl("/logout") .logoutUrl("/logout")
.logoutSuccessUrl("/"); .logoutSuccessUrl("/");
// @formatter:on // @formatter:on

View File

@ -13,7 +13,7 @@ public class ServletInitializer extends AbstractDispatcherServletInitializer {
@Override @Override
protected WebApplicationContext createServletApplicationContext() { protected WebApplicationContext createServletApplicationContext() {
final AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); final AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(PersistenceJPAConfig.class, WebConfig.class, SecurityConfig.class); context.register(PersistenceJPAConfig.class, WebConfig.class, SecurityConfig.class, ThymeleafConfig.class);
return context; return context;
} }

View File

@ -13,7 +13,7 @@ public class SessionListener implements HttpSessionListener {
@Override @Override
public void sessionCreated(HttpSessionEvent event) { public void sessionCreated(HttpSessionEvent event) {
logger.info("==== Session is created ===="); logger.info("==== Session is created ====");
event.getSession().setMaxInactiveInterval(30 * 60); event.getSession().setMaxInactiveInterval(60 * 60);
event.getSession().setAttribute("PREDICTION_FEATURE", MyFeatures.PREDICTION_FEATURE); event.getSession().setAttribute("PREDICTION_FEATURE", MyFeatures.PREDICTION_FEATURE);
} }

View File

@ -0,0 +1,39 @@
package org.baeldung.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import org.thymeleaf.templateresolver.TemplateResolver;
@Configuration
public class ThymeleafConfig {
@Bean
public TemplateResolver templateResolver() {
final ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
templateResolver.setPrefix("/WEB-INF/jsp/");
templateResolver.setSuffix(".jsp");
return templateResolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
final SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.addDialect(new SpringSecurityDialect());
return templateEngine;
}
@Bean
public ViewResolver viewResolver() {
final ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setOrder(1);
return viewResolver;
}
}

View File

@ -5,7 +5,10 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.baeldung.persistence.service.RedditTokenService; import javax.annotation.PostConstruct;
import org.apache.commons.lang.StringUtils;
import org.baeldung.persistence.dao.PostRepository;
import org.baeldung.reddit.classifier.RedditClassifier; import org.baeldung.reddit.classifier.RedditClassifier;
import org.baeldung.reddit.util.MyFeatures; import org.baeldung.reddit.util.MyFeatures;
import org.baeldung.reddit.util.UserAgentInterceptor; import org.baeldung.reddit.util.UserAgentInterceptor;
@ -115,6 +118,9 @@ public class WebConfig extends WebMvcConfigurerAdapter {
@Value("${clientSecret}") @Value("${clientSecret}")
private String clientSecret; private String clientSecret;
@Autowired
private PostRepository repo;
@Bean @Bean
public OAuth2ProtectedResourceDetails reddit() { public OAuth2ProtectedResourceDetails reddit() {
final AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails(); final AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
@ -132,16 +138,24 @@ public class WebConfig extends WebMvcConfigurerAdapter {
} }
@Bean @Bean
public OAuth2RestTemplate redditRestTemplate(OAuth2ClientContext clientContext, RedditTokenService redditTokenService) { public OAuth2RestTemplate redditRestTemplate(OAuth2ClientContext clientContext) {
final OAuth2RestTemplate template = new OAuth2RestTemplate(reddit(), clientContext); final OAuth2RestTemplate template = new OAuth2RestTemplate(reddit(), clientContext);
final List<ClientHttpRequestInterceptor> list = new ArrayList<ClientHttpRequestInterceptor>(); final List<ClientHttpRequestInterceptor> list = new ArrayList<ClientHttpRequestInterceptor>();
list.add(new UserAgentInterceptor()); list.add(new UserAgentInterceptor());
template.setInterceptors(list); template.setInterceptors(list);
final AccessTokenProviderChain accessTokenProvider = new AccessTokenProviderChain(Arrays.<AccessTokenProvider> asList(new MyAuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(), new ResourceOwnerPasswordAccessTokenProvider(), final AccessTokenProviderChain accessTokenProvider = new AccessTokenProviderChain(Arrays.<AccessTokenProvider> asList(new MyAuthorizationCodeAccessTokenProvider(), new ImplicitAccessTokenProvider(), new ResourceOwnerPasswordAccessTokenProvider(),
new ClientCredentialsAccessTokenProvider())); new ClientCredentialsAccessTokenProvider()));
accessTokenProvider.setClientTokenServices(redditTokenService);
template.setAccessTokenProvider(accessTokenProvider); template.setAccessTokenProvider(accessTokenProvider);
return template; return template;
} }
@PostConstruct
public void startupCheck() {
if (StringUtils.isBlank(accessTokenUri) || StringUtils.isBlank(userAuthorizationUri) || StringUtils.isBlank(clientID) || StringUtils.isBlank(clientSecret)) {
throw new RuntimeException("Incomplete reddit properties");
}
repo.findAll();
}
} }
} }

View File

@ -8,6 +8,4 @@ public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username); User findByUsername(String username);
User findByAccessToken(String token); User findByAccessToken(String token);
User findByRememberMeToken(String token);
} }

View File

@ -26,8 +26,6 @@ public class User {
private boolean needCaptcha; private boolean needCaptcha;
private String rememberMeToken;
public User() { public User() {
super(); super();
} }
@ -82,14 +80,6 @@ public class User {
// //
public String getRememberMeToken() {
return rememberMeToken;
}
public void setRememberMeToken(String rememberMeToken) {
this.rememberMeToken = rememberMeToken;
}
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;

View File

@ -1,55 +0,0 @@
package org.baeldung.persistence.service;
import org.baeldung.persistence.dao.UserRepository;
import org.baeldung.persistence.model.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.token.ClientTokenServices;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.stereotype.Component;
@Component
public class RedditTokenService implements ClientTokenServices {
@Autowired
private UserRepository userReopsitory;
private final Logger logger = LoggerFactory.getLogger(getClass());
public RedditTokenService() {
super();
}
@Override
public OAuth2AccessToken getAccessToken(OAuth2ProtectedResourceDetails resource, Authentication authentication) {
logger.info("reddit ==== getAccessToken");
final User user = (User) authentication.getPrincipal();
final DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(user.getAccessToken());
token.setRefreshToken(new DefaultOAuth2RefreshToken((user.getRefreshToken())));
token.setExpiration(user.getTokenExpiration());
return token;
}
@Override
public void saveAccessToken(OAuth2ProtectedResourceDetails resource, Authentication authentication, OAuth2AccessToken accessToken) {
logger.info("reddit ==== saveAccessToken");
final User user = (User) authentication.getPrincipal();
user.setAccessToken(accessToken.getValue());
if (accessToken.getRefreshToken() != null) {
user.setRefreshToken(accessToken.getRefreshToken().getValue());
}
user.setTokenExpiration(accessToken.getExpiration());
userReopsitory.save(user);
}
@Override
public void removeAccessToken(OAuth2ProtectedResourceDetails resource, Authentication authentication) {
logger.info("reddit ==== removeAccessToken");
}
}

View File

@ -8,11 +8,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.RandomStringUtils;
import org.baeldung.persistence.dao.PostRepository; import org.baeldung.persistence.dao.PostRepository;
import org.baeldung.persistence.dao.UserRepository; import org.baeldung.persistence.dao.UserRepository;
import org.baeldung.persistence.model.Post; import org.baeldung.persistence.model.Post;
@ -33,7 +28,6 @@ import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
@ -50,7 +44,6 @@ public class RedditController {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
private final SimpleDateFormat dfHour = new SimpleDateFormat("HH"); private final SimpleDateFormat dfHour = new SimpleDateFormat("HH");
public static final String REMEMBER_ME_COOKIE = "CustomRememberMe";
@Autowired @Autowired
@Qualifier("redditRestTemplate") @Qualifier("redditRestTemplate")
@ -66,11 +59,9 @@ public class RedditController {
private RedditClassifier redditClassifier; private RedditClassifier redditClassifier;
@RequestMapping("/login") @RequestMapping("/login")
public final String redditLogin(@CookieValue(value = REMEMBER_ME_COOKIE, required = false) String rememberMe, HttpServletRequest request, HttpServletResponse response) { public final String redditLogin() {
if (!canAutoLogin(rememberMe)) { final JsonNode node = redditRestTemplate.getForObject("https://oauth.reddit.com/api/v1/me", JsonNode.class);
final JsonNode node = redditRestTemplate.getForObject("https://oauth.reddit.com/api/v1/me", JsonNode.class); loadAuthentication(node.get("name").asText(), redditRestTemplate.getAccessToken());
loadAuthentication(node.get("name").asText(), redditRestTemplate.getAccessToken(), response);
}
return "redirect:home.html"; return "redirect:home.html";
} }
@ -229,14 +220,14 @@ public class RedditController {
return result; return result;
} else { } else {
if ((node.get("json").get("data") != null) && (node.get("json").get("data").get("url") != null)) { if ((node.get("json").get("data") != null) && (node.get("json").get("data").get("url") != null)) {
return "Post submitted successfully <a href=\"" + node.get("json").get("data").get("url").asText() + "\"> check it out </a>"; return "Post submitted successfully " + node.get("json").get("data").get("url").asText();
} else { } else {
return "Error Occurred"; return "Error Occurred";
} }
} }
} }
private void loadAuthentication(final String name, final OAuth2AccessToken token, HttpServletResponse response) { private void loadAuthentication(final String name, final OAuth2AccessToken token) {
User user = userReopsitory.findByUsername(name); User user = userReopsitory.findByUsername(name);
if (user == null) { if (user == null) {
user = new User(); user = new User();
@ -254,35 +245,8 @@ public class RedditController {
user.setTokenExpiration(token.getExpiration()); user.setTokenExpiration(token.getExpiration());
userReopsitory.save(user); userReopsitory.save(user);
generateRememberMeToken(user, response);
final UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, token.getValue(), Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"))); final UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, token.getValue(), Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
SecurityContextHolder.getContext().setAuthentication(auth); SecurityContextHolder.getContext().setAuthentication(auth);
} }
private void generateRememberMeToken(User user, HttpServletResponse response) {
String rememberMe = RandomStringUtils.randomAlphanumeric(30);
while (userReopsitory.findByRememberMeToken(rememberMe) != null) {
rememberMe = RandomStringUtils.randomAlphanumeric(30);
}
user.setRememberMeToken(rememberMe);
userReopsitory.save(user);
final Cookie c = new Cookie(REMEMBER_ME_COOKIE, rememberMe);
c.setMaxAge(1209600);
response.addCookie(c);
}
private boolean canAutoLogin(String rememberMeToken) {
if (rememberMeToken != null) {
final User user = userReopsitory.findByRememberMeToken(rememberMeToken);
if (user != null) {
logger.info("Auto Login successfully");
final UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, user.getAccessToken(), Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
SecurityContextHolder.getContext().setAuthentication(auth);
return true;
}
}
return false;
}
} }

View File

@ -1,10 +1,10 @@
################### DataSource Configuration ########################## ################### DataSource Configuration ##########################
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.driverClassName=org.postgresql.Driver
jdbc.url=jdbc:mysql://localhost:3306/oauth_reddit?createDatabaseIfNotExist=true jdbc.url=jdbc:postgres://fkqrbxdwqbrzhv:Y83Axvgz5iC_tyRZQxI0KEmUEG@ec2-107-20-222-114.compute-1.amazonaws.com:5432/dfmoej36meltnj
jdbc.user=tutorialuser jdbc.user=fkqrbxdwqbrzhv
jdbc.pass=tutorialmy5ql jdbc.pass=Y83Axvgz5iC_tyRZQxI0KEmUEG
################### Hibernate Configuration ########################## ################### Hibernate Configuration ##########################
hibernate.dialect=org.hibernate.dialect.MySQLDialect hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.show_sql=false hibernate.show_sql=false
hibernate.hbm2ddl.auto=update hibernate.hbm2ddl.auto=update

View File

@ -1,10 +1,10 @@
################### DataSource Configuration ########################## ################### DataSource Configuration ##########################
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.driverClassName=org.h2.Driver
jdbc.url=jdbc:mysql://localhost:3306/oauth_reddit?createDatabaseIfNotExist=true jdbc.url=jdbc:h2:mem:oauth_reddit;DB_CLOSE_DELAY=-1
jdbc.user=tutorialuser jdbc.user=sa
jdbc.pass=tutorialmy5ql jdbc.pass=
init-db=false
################### Hibernate Configuration ########################## ################### Hibernate Configuration ##########################
hibernate.dialect=org.hibernate.dialect.MySQLDialect hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.show_sql=false hibernate.show_sql=false
hibernate.hbm2ddl.auto=create-drop hibernate.hbm2ddl.auto=update

View File

@ -1,13 +1,12 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html> <html>
<head> <head>
<title>Schedule to Reddit</title> <title>Schedule to Reddit</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"/>
<link rel="stylesheet" href="<c:url value="/resources/datetime-picker.css" />"> <link rel="stylesheet" th:href="@{/resources/datetime-picker.css}" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="<c:url value="/resources/datetime-picker.js" />"></script> <script th:src="@{/resources/datetime-picker.js}"></script>
<script src="<c:url value="/resources/validator.js" />"></script> <script th:src="@{/resources/validator.js}"></script>
<style type="text/css"> <style type="text/css">
.btn.disabled{ .btn.disabled{
background-color: #ddd; background-color: #ddd;
@ -21,42 +20,46 @@ border-color: #ddd;
</style> </style>
</head> </head>
<body> <body>
<jsp:include page="header.jsp" /> <div th:include="header"/>
<div class="container"> <div class="container">
<h1>Edit Scheduled Post</h1> <h1>Edit Scheduled Post</h1>
<form action="<c:url value="/updatePost/${post.getId()}" />" method="post" role="form" data-toggle="validator"> <form th:action="@{/updatePost/{id}(id=${post.getId()})}" method="post" role="form" data-toggle="validator">
<div class="row"> <div class="row">
<input type="hidden" name="id" value="${post.getId()}"/> <input type="hidden" name="id" value="${post.getId()}"/>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3">Title</label> <label class="col-sm-3">Title</label>
<span class="col-sm-9"><input name="title" placeholder="title" class="form-control" value="${post.getTitle()}" required data-minlength="3"/></span> <span class="col-sm-9"><input name="title" placeholder="title" class="form-control" th:value="${post.getTitle()}" required="required" data-minlength="3"/></span>
</div> </div>
<br><br> <br/><br/>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3">Url</label> <label class="col-sm-3">Url</label>
<span class="col-sm-9"><input name="url" type="url" placeholder="url" class="form-control" value="${post.getUrl()}" required data-minlength="3"/></span> <span class="col-sm-9"><input name="url" type="url" placeholder="url" class="form-control" th:value="${post.getUrl()}" required="required" data-minlength="3"/></span>
</div> </div>
<br><br> <br/><br/>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3">Subreddit</label> <label class="col-sm-3">Subreddit</label>
<span class="col-sm-9"><input name="sr" placeholder="Subreddit" class="form-control" value="${post.getSubreddit()}" required data-minlength="3"/></span> <span class="col-sm-9"><input name="sr" placeholder="Subreddit" class="form-control" th:value="${post.getSubreddit()}" required="required" data-minlength="3"/></span>
</div> </div>
<br><br> <br/><br/>
<div> <div>
<label class="col-sm-3">Send replies to my inbox</label> <span class="col-sm-9"> <input type="checkbox" name="sendreplies" value="true" <c:if test="${post.isSendReplies()=='true'}"> checked </c:if> /></span> <label class="col-sm-3">Send replies to my inbox</label>
<span class="col-sm-9">
<input th:if="${post.isSendReplies()=='true'}" type="checkbox" name="sendreplies" value="true" checked="checked"/>
<input th:if="${post.isSendReplies()=='false'}" type="checkbox" name="sendreplies" value="true" checked="checked"/>
</span>
</div> </div>
<br><br> <br/><br/>
<label class="col-sm-3">Submission Date</label> <label class="col-sm-3">Submission Date</label>
<span class="col-sm-9"><input type="text" name="date" class="form-control" value="${dateValue}" readonly></span> <span class="col-sm-9"><input type="text" name="date" class="form-control" th:value="${dateValue}" readonly="readonly"/></span>
<script type="text/javascript"> <script type="text/javascript">
$(function(){ $(function(){
$('*[name=date]').appendDtpicker({"inline": true}); $('*[name=date]').appendDtpicker({"inline": true});
}); });
</script> </script>
<br><br> <br/><br/>
<button type="submit" class="btn btn-primary">Save Changes</button> <button type="submit" class="btn btn-primary">Save Changes</button>
</div> </div>

View File

@ -1,24 +1,23 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <div>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<nav class="navbar navbar-default"> <nav class="navbar navbar-default">
<div class="container-fluid"> <div class="container-fluid">
<div class="navbar-header"> <div class="navbar-header">
<a class="navbar-brand" href="<c:url value="/home.html" />">Schedule to Reddit</a> <a class="navbar-brand" th:href="@{/home.html}">Schedule to Reddit</a>
</div> </div>
<p class="navbar-text navbar-right">Logged in as <p class="navbar-text navbar-right">Logged in as
<b><sec:authentication property="principal.username" /></b>&nbsp;&nbsp;&nbsp; <b><span sec:authentication="principal.username">Bob</span></b>&nbsp;&nbsp;&nbsp;
<a href="<c:url value="/logout" />">Logout</a>&nbsp;&nbsp;&nbsp; <a th:href="@{/logout}">Logout</a>&nbsp;&nbsp;&nbsp;
</p> </p>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li><a href="<c:url value="/posts" />">My Scheduled Posts</a></li> <li><a th:href="@{/posts}">My Scheduled Posts</a></li>
<li><a href="<c:url value="/post" />">Post to Reddit</a></li> <li><a th:href="@{/post}">Post to Reddit</a></li>
<li><a href="<c:url value="/postSchedule" />">Schedule Post to Reddit</a></li> <li><a th:href="@{/postSchedule}">Schedule Post to Reddit</a></li>
</ul> </ul>
</div><!-- /.navbar-collapse --> </div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid --> </div><!-- /.container-fluid -->
</nav> </nav>
</div>

View File

@ -1,18 +1,16 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<html> <html>
<head> <head>
<title>Schedule to Reddit</title> <title>Schedule to Reddit</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"/>
</head> </head>
<body> <body>
<jsp:include page="header.jsp" /> <div th:include="header"/>
<div class="container"> <div class="container">
<h1>Welcome, <small><sec:authentication property="principal.username" /></small></h1> <h1>Welcome, <small><span sec:authentication="principal.username">Bob</span></small></h1>
<br> <br/>
<a href="posts" class="btn btn-primary">My Scheduled Posts</a> <a href="posts" class="btn btn-primary">My Scheduled Posts</a>
<a href="post" class="btn btn-primary">Post to Reddit</a> <a href="post" class="btn btn-primary">Post to Reddit</a>
<a href="postSchedule" class="btn btn-primary">Schedule Post to Reddit</a> <a href="postSchedule" class="btn btn-primary">Schedule Post to Reddit</a>

View File

@ -1,16 +1,12 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<html> <html>
<head> <head>
<title>Schedule to Reddit</title> <title>Schedule to Reddit</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"/>
</head> </head>
<body> <body>
<jsp:include page="header.jsp" /> <div th:include="header"/>
<div class="container"> <div class="container">
<h1>My Scheduled Posts</h1> <h1>My Scheduled Posts</h1>
@ -23,21 +19,20 @@
<th>Actions</th> <th>Actions</th>
</tr> </tr>
</thead> </thead>
<c:forEach var="post" items="${posts}" > <tr th:each="post : ${posts}">
<tr <c:if test="${post.isSent()}"> class="success"</c:if>> <td th:text="${post.getTitle()}"></td>
<td><c:out value="${post.getTitle()}"/></td> <td th:text="${#calendars.format(post.getSubmissionDate(),'dd MMMM yyyy HH:mm z')}"></td>
<td><fmt:formatDate type="both" dateStyle="long" timeStyle="long" value="${post.getSubmissionDate()}" /></td> <td th:text="${post.getSubmissionResponse()}"></td>
<td><c:out value="${post.getSubmissionResponse()}"/></td>
<td> <td>
<a href="editPost/${post.getId()}" class="btn btn-warning" >Edit</a> <a th:href="@{/editPost/{id}(id=${post.getId()})}" class="btn btn-warning" >Edit</a>
<a href="#" class="btn btn-danger" onclick="confirmDelete(${post.getId()})">Delete</a> <a href="#" class="btn btn-danger" th:onclick="'javascript:confirmDelete(\'' +${post.getId()}+ '\') '">Delete</a>
</td> </td>
</tr> </tr>
</c:forEach>
</table> </table>
</div> </div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script> <script>
/*<![CDATA[*/
function confirmDelete(id) { function confirmDelete(id) {
if (confirm("Do you really want to delete this post?") == true) { if (confirm("Do you really want to delete this post?") == true) {
deletePost(id); deletePost(id);
@ -53,6 +48,7 @@ function deletePost(id){
} }
}); });
} }
/*]]>*/
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,16 +1,13 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<html> <html>
<head> <head>
<title>Schedule to Reddit</title> <title>Schedule to Reddit</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"/>
<link rel="stylesheet" href="<c:url value="/resources/datetime-picker.css" />"> <link rel="stylesheet" th:href="@{/resources/datetime-picker.css}"/>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<script src="<c:url value="/resources/datetime-picker.js" />"></script> <script th:src="@{/resources/datetime-picker.js}"></script>
<script src="<c:url value="/resources/validator.js" />"></script> <script th:src="@{/resources/validator.js}"></script>
<style type="text/css"> <style type="text/css">
.btn.disabled{ .btn.disabled{
background-color: #ddd; background-color: #ddd;
@ -24,7 +21,7 @@ border-color: #ddd;
</style> </style>
</head> </head>
<body> <body>
<jsp:include page="header.jsp" /> <div th:include="header"/>
<div class="container"> <div class="container">
<h1>Schedule Post to Reddit</h1> <h1>Schedule Post to Reddit</h1>
@ -32,36 +29,36 @@ border-color: #ddd;
<div class="row"> <div class="row">
<div class="form-group"> <div class="form-group">
<label class="col-sm-3">Title</label> <label class="col-sm-3">Title</label>
<span class="col-sm-9"><input name="title" placeholder="title" class="form-control" required data-minlength="3"/></span> <span class="col-sm-9"><input name="title" placeholder="title" class="form-control" required="required" data-minlength="3"/></span>
</div> </div>
<br><br> <br/><br/>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3">Url</label> <label class="col-sm-3">Url</label>
<span class="col-sm-9"><input name="url" type="url" placeholder="url" class="form-control" required data-minlength="3"/></span> <span class="col-sm-9"><input name="url" type="url" placeholder="url" class="form-control" required="required" data-minlength="3"/></span>
</div> </div>
<br><br> <br/><br/>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3">Subreddit</label> <label class="col-sm-3">Subreddit</label>
<span class="col-sm-9"><input name="sr" placeholder="Subreddit (e.g. kitten)" class="form-control" required data-minlength="3"/></span> <span class="col-sm-9"><input name="sr" placeholder="Subreddit (e.g. kitten)" class="form-control" required="required" data-minlength="3"/></span>
</div> </div>
<br><br> <br/><br/>
<div> <div>
<label class="col-sm-3">Send replies to my inbox</label> <span class="col-sm-9"><input type="checkbox" name="sendreplies" value="true"/></span> <label class="col-sm-3">Send replies to my inbox</label> <span class="col-sm-9"><input type="checkbox" name="sendreplies" value="true"/></span>
</div> </div>
<br> <br/>
<hr/> <hr/>
<br> <br/>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3">Resubmit If:</label> <label class="col-sm-3">Resubmit If:</label>
<span class="col-sm-2">Votes didn't exceed </span> <span class="col-sm-2">Votes didn't exceed </span>
<span class="col-sm-1"> <span class="col-sm-1">
<input type="number" class="form-control input-sm" value="0" name="score" required/> <input type="number" class="form-control input-sm" value="0" name="score" required="required"/>
</span> </span>
<span class="col-sm-3">within &nbsp;&nbsp; <span class="col-sm-3">within &nbsp;&nbsp;
<select name="interval"> <select name="interval">
<option value="0" selected>None</option> <option value="0" selected="selected">None</option>
<option value="45">45 minutes</option> <option value="45">45 minutes</option>
<option value="60">1 hour</option> <option value="60">1 hour</option>
<option value="120">2 hours</option> <option value="120">2 hours</option>
@ -70,7 +67,7 @@ border-color: #ddd;
<span class="col-sm-3">try resubmitting &nbsp;&nbsp; <span class="col-sm-3">try resubmitting &nbsp;&nbsp;
<select name="attempt"> <select name="attempt">
<option value="0" selected>No</option> <option value="0" selected="selected">No</option>
<option value="2">2</option> <option value="2">2</option>
<option value="3">3</option> <option value="3">3</option>
<option value="4">4</option> <option value="4">4</option>
@ -81,18 +78,20 @@ border-color: #ddd;
</div> </div>
<br><br> <br/><br/>
<label class="col-sm-3">Submission Date</label> <label class="col-sm-3">Submission Date</label>
<span class="col-sm-9"><input type="text" name="date" class="form-control" readonly></span> <span class="col-sm-9"><input type="text" name="date" class="form-control" readonly="readonly"/></span>
<script type="text/javascript"> <script type="text/javascript">
/*<![CDATA[*/
$(function(){ $(function(){
$('*[name=date]').appendDtpicker({"inline": true}); $('*[name=date]').appendDtpicker({"inline": true});
}); });
/*]]>*/
</script> </script>
<br><br> <br/><br/>
<button type="submit" class="btn btn-primary">Schedule</button> <button type="submit" class="btn btn-primary">Schedule</button>

View File

@ -1,13 +1,10 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<html> <html>
<head> <head>
<title>Schedule to Reddit</title> <title>Schedule to Reddit</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"/>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="<c:url value="/resources/validator.js" />"></script> <script th:src="@{/resources/validator.js}"></script>
<style type="text/css"> <style type="text/css">
.btn.disabled{ .btn.disabled{
background-color: #ddd; background-color: #ddd;
@ -21,7 +18,7 @@ border-color: #ddd;
</style> </style>
</head> </head>
<body> <body>
<jsp:include page="header.jsp" /> <div th:include="header"/>
<div class="container"> <div class="container">
<h1>Post to Reddit</h1> <h1>Post to Reddit</h1>
@ -29,45 +26,46 @@ border-color: #ddd;
<div class="row"> <div class="row">
<div class="form-group"> <div class="form-group">
<label class="col-sm-3">Title</label> <label class="col-sm-3">Title</label>
<span class="col-sm-9"><input name="title" placeholder="title" class="form-control" required data-minlength="3"/></span> <span class="col-sm-9"><input name="title" placeholder="title" class="form-control" data-minlength="3" required="required"/></span>
</div> </div>
<br><br> <br/><br/>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3">Url</label> <label class="col-sm-3">Url</label>
<span class="col-sm-9"><input name="url" type="url" placeholder="url" class="form-control" required data-minlength="3"/></span> <span class="col-sm-9"><input name="url" type="url" placeholder="url" class="form-control" data-minlength="3" required="required"/></span>
</div> </div>
<br><br> <br/><br/>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3">Subreddit</label> <label class="col-sm-3">Subreddit</label>
<span class="col-sm-9"><input name="sr" placeholder="Subreddit (e.g. kitten)" class="form-control" required data-minlength="3"/></span> <span class="col-sm-9"><input name="sr" placeholder="Subreddit (e.g. kitten)" class="form-control" data-minlength="3" required="required"/></span>
</div> </div>
<br><br> <br/><br/>
<div> <div>
<label class="col-sm-3">Send replies to my inbox</label> <span class="col-sm-9"><input type="checkbox" name="sendreplies" value="true"/></span> <label class="col-sm-3">Send replies to my inbox</label> <span class="col-sm-9"><input type="checkbox" name="sendreplies" value="true"/></span>
</div> </div>
<br><br> <br/><br/>
<c:if test="${iden != null}"> <div th:if="${iden != null}">
<input type="hidden" name="iden" value="${iden}"/> <input type="hidden" name="iden" value="${iden}"/>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3">Captcha</label> <label class="col-sm-3">Captcha</label>
<span class="col-sm-9"><input name="captcha" placeholder="captcha" class="form-control"/></span> <span class="col-sm-9"><input name="captcha" placeholder="captcha" class="form-control"/></span>
</div> </div>
<br><br> <br/><br/>
<img src="http://www.reddit.com/captcha/${iden}" alt="captcha" width="200"/> <img src="http://www.reddit.com/captcha/${iden}" alt="captcha" width="200"/>
</c:if> </div>
<br><br> <br/><br/>
<span class="col-sm-3"><button id="submitbtn" type="submit" class="btn btn-primary">Post</button></span> <span class="col-sm-3"><button id="submitbtn" type="submit" class="btn btn-primary">Post</button></span>
</div> </div>
</form> </form>
<div> <div>
<c:if test="${PREDICTION_FEATURE.isActive()}"> <div th:if="${session.PREDICTION_FEATURE.isActive()}">
<button id="checkbtn" class="btn btn-default disabled" onclick="predicateResponse()">Predicate Response</button> <button id="checkbtn" class="btn btn-default disabled" onclick="predicateResponse()">Predicate Response</button>
<span id="prediction"></span> <span id="prediction"></span>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
/*<![CDATA[*/
$("input").change(function() { $("input").change(function() {
if($("#submitbtn").hasClass("disabled")){ if($("#submitbtn").hasClass("disabled")){
if(! $("#checkbtn").hasClass("disabled")){ if(! $("#checkbtn").hasClass("disabled")){
@ -82,12 +80,13 @@ function predicateResponse(){
var domain = $('input[name="url"]').val(); var domain = $('input[name="url"]').val();
domain = $('<a>').prop('href', domain).prop('hostname'); domain = $('<a>').prop('href', domain).prop('hostname');
console.log(domain); console.log(domain);
$.post("<c:url value="/predicatePostResponse"></c:url>",{title: title, domain: domain} ,function(data){ $.post("predicatePostResponse",{title: title, domain: domain} ,function(data){
$("#prediction").addClass("alert alert-info").html(data.replace('{','').replace('}','')); $("#prediction").addClass("alert alert-info").html(data.replace('{','').replace('}',''));
}); });
} }
/*]]>*/
</script> </script>
</c:if> </div>
</div> </div>
</div> </div>
</body> </body>

View File

@ -1,17 +1,14 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<html> <html>
<head> <head>
<title>Schedule to Reddit</title> <title>Schedule to Reddit</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"/>
</head> </head>
<body> <body>
<jsp:include page="header.jsp" /> <div th:include="header"/>
<div class="container"> <div class="container">
<h1>${msg}</h1> <h1 th:text="${msg}">Hello</h1>
</div> </div>
</body> </body>
</html> </html>

View File

@ -1,11 +1,9 @@
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Schedule to Reddit</title> <title>Schedule to Reddit</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"/>
</head> </head>
<body> <body>
<div class="container"> <div class="container">

View File

@ -6,9 +6,10 @@ import java.io.IOException;
import org.baeldung.reddit.classifier.RedditClassifier; import org.baeldung.reddit.classifier.RedditClassifier;
import org.baeldung.reddit.classifier.RedditDataCollector; import org.baeldung.reddit.classifier.RedditDataCollector;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
//@Ignore @Ignore
public class RedditClassifierTest { public class RedditClassifierTest {
@Test @Test

View File

@ -8,13 +8,11 @@ import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.List; import java.util.List;
import org.baeldung.config.PersistenceJPAConfig;
import org.baeldung.persistence.dao.PostRepository; import org.baeldung.persistence.dao.PostRepository;
import org.baeldung.persistence.dao.UserRepository; import org.baeldung.persistence.dao.UserRepository;
import org.baeldung.persistence.model.Post; import org.baeldung.persistence.model.Post;
import org.baeldung.persistence.model.User; import org.baeldung.persistence.model.User;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -24,10 +22,10 @@ import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { PersistenceJPAConfig.class }) @ContextConfiguration(classes = { TestJPAConfig.class })
@Transactional @Transactional
@TransactionConfiguration @TransactionConfiguration
@Ignore // @Ignore
public class PersistenceJPATest { public class PersistenceJPATest {
@Autowired @Autowired
private PostRepository postRepository; private PostRepository postRepository;

View File

@ -0,0 +1,73 @@
package org.baeldung.persistence;
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
@PropertySource({ "classpath:persistence-test.properties" })
@ComponentScan({ "org.baeldung.persistence.model", "org.baeldung.persistence.dao" })
@EnableJpaRepositories(basePackages = "org.baeldung.persistence.dao")
public class TestJPAConfig {
@Autowired
private Environment env;
public TestJPAConfig() {
super();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "org.baeldung.persistence.model" });
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
@Bean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.user"));
dataSource.setPassword(env.getProperty("jdbc.pass"));
return dataSource;
}
@Bean
public JpaTransactionManager transactionManager() {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
final Properties additionalProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto", "create-drop"));
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
return hibernateProperties;
}
}