Merge pull request #153 from Doha2012/master
add schedule post to reddit
This commit is contained in:
		
						commit
						cc759f1cf9
					
				| @ -23,6 +23,9 @@ import org.springframework.util.MultiValueMap; | |||||||
| 
 | 
 | ||||||
| public class MyAuthorizationCodeAccessTokenProvider extends AuthorizationCodeAccessTokenProvider implements Serializable { | public class MyAuthorizationCodeAccessTokenProvider extends AuthorizationCodeAccessTokenProvider implements Serializable { | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      */ | ||||||
|     private static final long serialVersionUID = 3822611002661972274L; |     private static final long serialVersionUID = 3822611002661972274L; | ||||||
| 
 | 
 | ||||||
|     private StateKeyGenerator stateKeyGenerator = new DefaultStateKeyGenerator(); |     private StateKeyGenerator stateKeyGenerator = new DefaultStateKeyGenerator(); | ||||||
|  | |||||||
| @ -0,0 +1,75 @@ | |||||||
|  | package org.baeldung.config; | ||||||
|  | 
 | ||||||
|  | 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.properties" }) | ||||||
|  | @ComponentScan({ "org.baeldung.persistence" }) | ||||||
|  | @EnableJpaRepositories(basePackages = "org.baeldung.persistence.dao") | ||||||
|  | public class PersistenceJPAConfig { | ||||||
|  | 
 | ||||||
|  |     @Autowired | ||||||
|  |     private Environment env; | ||||||
|  | 
 | ||||||
|  |     public PersistenceJPAConfig() { | ||||||
|  |         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")); | ||||||
|  |         hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect")); | ||||||
|  |         return hibernateProperties; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -13,7 +13,7 @@ public class ServletInitializer extends AbstractDispatcherServletInitializer { | |||||||
|     @Override |     @Override | ||||||
|     protected WebApplicationContext createServletApplicationContext() { |     protected WebApplicationContext createServletApplicationContext() { | ||||||
|         AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); |         AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); | ||||||
|         context.register(WebConfig.class); |         context.register(PersistenceJPAConfig.class, WebConfig.class); | ||||||
|         return context; |         return context; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,12 +2,15 @@ package org.baeldung.config; | |||||||
| 
 | 
 | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| 
 | 
 | ||||||
|  | import org.baeldung.web.schedule.ScheduledTasks; | ||||||
| import org.springframework.beans.factory.annotation.Value; | import org.springframework.beans.factory.annotation.Value; | ||||||
| import org.springframework.context.annotation.Bean; | import org.springframework.context.annotation.Bean; | ||||||
| import org.springframework.context.annotation.ComponentScan; | import org.springframework.context.annotation.ComponentScan; | ||||||
| import org.springframework.context.annotation.Configuration; | import org.springframework.context.annotation.Configuration; | ||||||
| import org.springframework.context.annotation.PropertySource; | import org.springframework.context.annotation.PropertySource; | ||||||
| import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; | import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; | ||||||
|  | import org.springframework.scheduling.annotation.EnableAsync; | ||||||
|  | import org.springframework.scheduling.annotation.EnableScheduling; | ||||||
| import org.springframework.security.oauth2.client.OAuth2ClientContext; | import org.springframework.security.oauth2.client.OAuth2ClientContext; | ||||||
| import org.springframework.security.oauth2.client.OAuth2RestTemplate; | import org.springframework.security.oauth2.client.OAuth2RestTemplate; | ||||||
| import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; | import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; | ||||||
| @ -27,7 +30,9 @@ import org.springframework.web.servlet.view.InternalResourceViewResolver; | |||||||
| 
 | 
 | ||||||
| @Configuration | @Configuration | ||||||
| @EnableWebMvc | @EnableWebMvc | ||||||
| @ComponentScan(basePackages = { "org.baeldung.web" }) | @EnableScheduling | ||||||
|  | @EnableAsync | ||||||
|  | @ComponentScan({ "org.baeldung.web" }) | ||||||
| public class WebConfig extends WebMvcConfigurerAdapter { | public class WebConfig extends WebMvcConfigurerAdapter { | ||||||
| 
 | 
 | ||||||
|     @Bean |     @Bean | ||||||
| @ -48,6 +53,25 @@ public class WebConfig extends WebMvcConfigurerAdapter { | |||||||
|         configurer.enable(); |         configurer.enable(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // @Bean | ||||||
|  |     // public RedditController redditController(OAuth2RestTemplate redditRestTemplate) { | ||||||
|  |     // RedditController controller = new RedditController(); | ||||||
|  |     // controller.setRedditRestTemplate(redditRestTemplate); | ||||||
|  |     // return controller; | ||||||
|  |     // } | ||||||
|  |     // | ||||||
|  |     // @Bean | ||||||
|  |     // public RestExceptionHandler restExceptionHandler() { | ||||||
|  |     // return new RestExceptionHandler(); | ||||||
|  |     // } | ||||||
|  |     // | ||||||
|  |     @Bean | ||||||
|  |     public ScheduledTasks scheduledTasks(OAuth2ProtectedResourceDetails reddit) { | ||||||
|  |         ScheduledTasks s = new ScheduledTasks(); | ||||||
|  |         s.setRedditRestTemplate(new OAuth2RestTemplate(reddit)); | ||||||
|  |         return s; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public void addResourceHandlers(ResourceHandlerRegistry registry) { |     public void addResourceHandlers(ResourceHandlerRegistry registry) { | ||||||
|         registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); |         registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -0,0 +1,15 @@ | |||||||
|  | package org.baeldung.persistence.dao; | ||||||
|  | 
 | ||||||
|  | import java.util.Date; | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
|  | import org.baeldung.persistence.model.Post; | ||||||
|  | import org.baeldung.persistence.model.User; | ||||||
|  | import org.springframework.data.jpa.repository.JpaRepository; | ||||||
|  | 
 | ||||||
|  | public interface PostRepository extends JpaRepository<Post, Long> { | ||||||
|  | 
 | ||||||
|  |     public List<Post> findBySubmissionDateBefore(Date date); | ||||||
|  | 
 | ||||||
|  |     public List<Post> findByUser(User user); | ||||||
|  | } | ||||||
| @ -0,0 +1,10 @@ | |||||||
|  | package org.baeldung.persistence.dao; | ||||||
|  | 
 | ||||||
|  | import org.baeldung.persistence.model.User; | ||||||
|  | import org.springframework.data.jpa.repository.JpaRepository; | ||||||
|  | 
 | ||||||
|  | public interface UserRepository extends JpaRepository<User, Long> { | ||||||
|  |     public User findByUsername(String username); | ||||||
|  | 
 | ||||||
|  |     public User findByAccessToken(String token); | ||||||
|  | } | ||||||
| @ -0,0 +1,103 @@ | |||||||
|  | package org.baeldung.persistence.model; | ||||||
|  | 
 | ||||||
|  | import java.util.Date; | ||||||
|  | 
 | ||||||
|  | import javax.persistence.Entity; | ||||||
|  | import javax.persistence.GeneratedValue; | ||||||
|  | import javax.persistence.GenerationType; | ||||||
|  | import javax.persistence.Id; | ||||||
|  | import javax.persistence.JoinColumn; | ||||||
|  | import javax.persistence.ManyToOne; | ||||||
|  | 
 | ||||||
|  | @Entity | ||||||
|  | public class Post { | ||||||
|  | 
 | ||||||
|  |     @Id | ||||||
|  |     @GeneratedValue(strategy = GenerationType.AUTO) | ||||||
|  |     private Long id; | ||||||
|  | 
 | ||||||
|  |     private String title; | ||||||
|  | 
 | ||||||
|  |     private String subreddit; | ||||||
|  | 
 | ||||||
|  |     private String url; | ||||||
|  | 
 | ||||||
|  |     private Date submissionDate; | ||||||
|  | 
 | ||||||
|  |     private boolean isSent; | ||||||
|  | 
 | ||||||
|  |     private String submissionResponse; | ||||||
|  | 
 | ||||||
|  |     @ManyToOne | ||||||
|  |     @JoinColumn(name = "user_id") | ||||||
|  |     private User user; | ||||||
|  | 
 | ||||||
|  |     public Post() { | ||||||
|  |         super(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Long getId() { | ||||||
|  |         return id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setId(final Long id) { | ||||||
|  |         this.id = id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getTitle() { | ||||||
|  |         return title; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setTitle(String title) { | ||||||
|  |         this.title = title; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getSubreddit() { | ||||||
|  |         return subreddit; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setSubreddit(String subreddit) { | ||||||
|  |         this.subreddit = subreddit; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getUrl() { | ||||||
|  |         return url; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setUrl(String url) { | ||||||
|  |         this.url = url; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Date getSubmissionDate() { | ||||||
|  |         return submissionDate; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setSubmissionDate(Date submissionDate) { | ||||||
|  |         this.submissionDate = submissionDate; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public User getUser() { | ||||||
|  |         return user; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setUser(User user) { | ||||||
|  |         this.user = user; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public boolean isSent() { | ||||||
|  |         return isSent; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setSent(boolean isSent) { | ||||||
|  |         this.isSent = isSent; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getSubmissionResponse() { | ||||||
|  |         return submissionResponse; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setSubmissionResponse(String submissionResponse) { | ||||||
|  |         this.submissionResponse = submissionResponse; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,104 @@ | |||||||
|  | package org.baeldung.persistence.model; | ||||||
|  | 
 | ||||||
|  | import java.util.Date; | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
|  | import javax.persistence.Entity; | ||||||
|  | import javax.persistence.GeneratedValue; | ||||||
|  | import javax.persistence.GenerationType; | ||||||
|  | import javax.persistence.Id; | ||||||
|  | import javax.persistence.OneToMany; | ||||||
|  | 
 | ||||||
|  | @Entity | ||||||
|  | public class User { | ||||||
|  | 
 | ||||||
|  |     @Id | ||||||
|  |     @GeneratedValue(strategy = GenerationType.AUTO) | ||||||
|  |     private Long id; | ||||||
|  | 
 | ||||||
|  |     private String username; | ||||||
|  | 
 | ||||||
|  |     private String accessToken; | ||||||
|  | 
 | ||||||
|  |     private String refreshToken; | ||||||
|  | 
 | ||||||
|  |     private Date tokenExpiration; | ||||||
|  | 
 | ||||||
|  |     @OneToMany(mappedBy = "user") | ||||||
|  |     private List<Post> posts; | ||||||
|  | 
 | ||||||
|  |     public User() { | ||||||
|  |         super(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Long getId() { | ||||||
|  |         return id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setId(final Long id) { | ||||||
|  |         this.id = id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getUsername() { | ||||||
|  |         return username; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setUsername(String username) { | ||||||
|  |         this.username = username; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getAccessToken() { | ||||||
|  |         return accessToken; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setAccessToken(String accessToken) { | ||||||
|  |         this.accessToken = accessToken; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getRefreshToken() { | ||||||
|  |         return refreshToken; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setRefreshToken(String refreshToken) { | ||||||
|  |         this.refreshToken = refreshToken; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Date getTokenExpiration() { | ||||||
|  |         return tokenExpiration; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setTokenExpiration(Date tokenExpiration) { | ||||||
|  |         this.tokenExpiration = tokenExpiration; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public List<Post> getPosts() { | ||||||
|  |         return posts; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setPosts(List<Post> posts) { | ||||||
|  |         this.posts = posts; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public int hashCode() { | ||||||
|  |         final int prime = 31; | ||||||
|  |         int result = 1; | ||||||
|  |         result = prime * result + ((username == null) ? 0 : username.hashCode()); | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public boolean equals(final Object obj) { | ||||||
|  |         if (this == obj) | ||||||
|  |             return true; | ||||||
|  |         if (obj == null) | ||||||
|  |             return false; | ||||||
|  |         if (getClass() != obj.getClass()) | ||||||
|  |             return false; | ||||||
|  |         final User user = (User) obj; | ||||||
|  |         if (!username.equals(user.username)) | ||||||
|  |             return false; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -1,15 +1,21 @@ | |||||||
| package org.baeldung.web; | package org.baeldung.web; | ||||||
| 
 | 
 | ||||||
| import java.io.IOException; | import java.text.ParseException; | ||||||
| import java.util.ArrayList; | import java.text.SimpleDateFormat; | ||||||
|  | import java.util.Date; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
|  | import org.baeldung.persistence.dao.PostRepository; | ||||||
|  | import org.baeldung.persistence.dao.UserRepository; | ||||||
|  | import org.baeldung.persistence.model.Post; | ||||||
|  | import org.baeldung.persistence.model.User; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||||
| import org.springframework.security.oauth2.client.OAuth2RestTemplate; | import org.springframework.security.oauth2.client.OAuth2RestTemplate; | ||||||
|  | import org.springframework.security.oauth2.common.OAuth2AccessToken; | ||||||
| import org.springframework.stereotype.Controller; | 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; | ||||||
| @ -17,37 +23,43 @@ import org.springframework.util.MultiValueMap; | |||||||
| import org.springframework.web.bind.annotation.RequestMapping; | import org.springframework.web.bind.annotation.RequestMapping; | ||||||
| import org.springframework.web.bind.annotation.RequestParam; | import org.springframework.web.bind.annotation.RequestParam; | ||||||
| 
 | 
 | ||||||
| import com.fasterxml.jackson.core.JsonProcessingException; |  | ||||||
| import com.fasterxml.jackson.databind.JsonNode; | import com.fasterxml.jackson.databind.JsonNode; | ||||||
| import com.fasterxml.jackson.databind.ObjectMapper; |  | ||||||
| 
 | 
 | ||||||
| @Controller | @Controller | ||||||
| public class RedditController { | public class RedditController { | ||||||
| 
 | 
 | ||||||
|     private final Logger logger = LoggerFactory.getLogger(getClass()); |     private final Logger logger = LoggerFactory.getLogger(getClass()); | ||||||
| 
 | 
 | ||||||
|  |     private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); | ||||||
|  | 
 | ||||||
|     @Autowired |     @Autowired | ||||||
|     private OAuth2RestTemplate redditRestTemplate; |     private OAuth2RestTemplate redditRestTemplate; | ||||||
| 
 | 
 | ||||||
|     // API |     @Autowired | ||||||
|  |     private UserRepository userReopsitory; | ||||||
|  | 
 | ||||||
|  |     @Autowired | ||||||
|  |     private PostRepository postReopsitory; | ||||||
| 
 | 
 | ||||||
|     @RequestMapping("/info") |     @RequestMapping("/info") | ||||||
|     public final String getInfo(Model model) { |     public final String getInfo(Model model) { | ||||||
|         final JsonNode node = redditRestTemplate.getForObject("https://oauth.reddit.com/api/v1/me", JsonNode.class); |         JsonNode node = redditRestTemplate.getForObject("https://oauth.reddit.com/api/v1/me", JsonNode.class); | ||||||
|         final String name = node.get("name").asText(); |         String name = node.get("name").asText(); | ||||||
|  |         addUser(name, redditRestTemplate.getAccessToken()); | ||||||
|         model.addAttribute("info", name); |         model.addAttribute("info", name); | ||||||
|         return "reddit"; |         return "reddit"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @RequestMapping("/submit") |     @RequestMapping("/submit") | ||||||
|     public final String submit(Model model, @RequestParam Map<String, String> formParams) { |     public final String submit(Model model, @RequestParam Map<String, String> formParams) { | ||||||
|         final MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>(); |         MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>(); | ||||||
|         param.add("api_type", "json"); |         param.add("api_type", "json"); | ||||||
|         param.add("kind", "link"); |         param.add("kind", "link"); | ||||||
|         param.add("resubmit", "true"); |         param.add("resubmit", "true"); | ||||||
|         param.add("sendreplies", "false"); |         param.add("sendreplies", "false"); | ||||||
|         param.add("then", "comments"); |         param.add("then", "comments"); | ||||||
|         for (final Map.Entry<String, String> entry : formParams.entrySet()) { | 
 | ||||||
|  |         for (Map.Entry<String, String> entry : formParams.entrySet()) { | ||||||
|             param.add(entry.getKey(), entry.getValue()); |             param.add(entry.getKey(), entry.getValue()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -60,44 +72,73 @@ public class RedditController { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @RequestMapping("/post") |     @RequestMapping("/post") | ||||||
|     public final String showSubmissionForm(final Model model) { |     public final String showSubmissionForm(Model model) { | ||||||
|         final String needsCaptchaResult = needsCaptcha(); |         String needsCaptchaResult = needsCaptcha(); | ||||||
|         if (needsCaptchaResult.equalsIgnoreCase("true")) { |         if (needsCaptchaResult.equalsIgnoreCase("true")) { | ||||||
|             final String iden = getNewCaptcha(); |             String iden = getNewCaptcha(); | ||||||
|             model.addAttribute("iden", iden); |             model.addAttribute("iden", iden); | ||||||
|         } |         } | ||||||
|         return "submissionForm"; |         return "submissionForm"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // === private |     @RequestMapping("/postSchedule") | ||||||
|  |     public final String showSchedulePostForm(Model model) { | ||||||
|  |         String needsCaptchaResult = needsCaptcha(); | ||||||
|  |         if (needsCaptchaResult.equalsIgnoreCase("true")) { | ||||||
|  |             model.addAttribute("msg", "Sorry, You do not have enought karma"); | ||||||
|  |             return "submissionResponse"; | ||||||
|  |         } | ||||||
|  |         return "schedulePostForm"; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     final List<String> getSubreddit() throws JsonProcessingException, IOException { |     @RequestMapping("/schedule") | ||||||
|         final String result = redditRestTemplate.getForObject("https://oauth.reddit.com/subreddits/popular?limit=50", String.class); |     public final String schedule(Model model, @RequestParam Map<String, String> formParams) throws ParseException { | ||||||
|         final JsonNode node = new ObjectMapper().readTree(result).get("data").get("children"); |         logger.info("User scheduling Post with these parameters: " + formParams.entrySet()); | ||||||
|         final List<String> subreddits = new ArrayList<String>(); |         User user = userReopsitory.findByAccessToken(redditRestTemplate.getAccessToken().getValue()); | ||||||
|         for (JsonNode child : node) { |         Post post = new Post(); | ||||||
|             subreddits.add(child.get("data").get("display_name").asText()); |         post.setUser(user); | ||||||
|  |         post.setSent(false); | ||||||
|  |         post.setTitle(formParams.get("title")); | ||||||
|  |         post.setSubreddit(formParams.get("sr")); | ||||||
|  |         post.setUrl(formParams.get("url")); | ||||||
|  |         post.setSubmissionDate(dateFormat.parse(formParams.get("date"))); | ||||||
|  |         if (post.getSubmissionDate().before(new Date())) { | ||||||
|  |             model.addAttribute("msg", "Invalid date"); | ||||||
|  |             return "submissionResponse"; | ||||||
|         } |         } | ||||||
|         return subreddits; |         postReopsitory.save(post); | ||||||
|  |         List<Post> posts = postReopsitory.findByUser(user); | ||||||
|  |         model.addAttribute("posts", posts); | ||||||
|  |         return "postListView"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @RequestMapping("/posts") | ||||||
|  |     public final String getScheduledPosts(Model model) { | ||||||
|  |         User user = userReopsitory.findByAccessToken(redditRestTemplate.getAccessToken().getValue()); | ||||||
|  |         List<Post> posts = postReopsitory.findByUser(user); | ||||||
|  |         model.addAttribute("posts", posts); | ||||||
|  |         return "postListView"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // === private | ||||||
|  | 
 | ||||||
|     private final String needsCaptcha() { |     private final String needsCaptcha() { | ||||||
|         String result = redditRestTemplate.getForObject("https://oauth.reddit.com/api/needs_captcha.json", String.class); |         String result = redditRestTemplate.getForObject("https://oauth.reddit.com/api/needs_captcha.json", String.class); | ||||||
|         return result; |         return result; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private final String getNewCaptcha() { |     private final String getNewCaptcha() { | ||||||
|         final Map<String, String> param = new HashMap<String, String>(); |         Map<String, String> param = new HashMap<String, String>(); | ||||||
|         param.put("api_type", "json"); |         param.put("api_type", "json"); | ||||||
| 
 | 
 | ||||||
|         final String result = redditRestTemplate.postForObject("https://oauth.reddit.com/api/new_captcha", param, String.class, param); |         String result = redditRestTemplate.postForObject("https://oauth.reddit.com/api/new_captcha", param, String.class, param); | ||||||
|         final String[] split = result.split("\""); |         String[] split = result.split("\""); | ||||||
|         return split[split.length - 2]; |         return split[split.length - 2]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private final String parseResponse(final JsonNode node) { |     private final String parseResponse(JsonNode node) { | ||||||
|         String result = ""; |         String result = ""; | ||||||
|         final JsonNode errorNode = node.get("json").get("errors").get(0); |         JsonNode errorNode = node.get("json").get("errors").get(0); | ||||||
|         if (errorNode != null) { |         if (errorNode != null) { | ||||||
|             for (JsonNode child : errorNode) { |             for (JsonNode child : errorNode) { | ||||||
|                 result = result + child.toString().replaceAll("\"|null", "") + "<br>"; |                 result = result + child.toString().replaceAll("\"|null", "") + "<br>"; | ||||||
| @ -111,4 +152,16 @@ public class RedditController { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private final void addUser(String name, OAuth2AccessToken token) { | ||||||
|  |         User user = userReopsitory.findByUsername(name); | ||||||
|  |         if (user == null) { | ||||||
|  |             user = new User(); | ||||||
|  |             user.setUsername(name); | ||||||
|  |             user.setAccessToken(token.getValue()); | ||||||
|  |             user.setRefreshToken(token.getRefreshToken().getValue()); | ||||||
|  |             user.setTokenExpiration(token.getExpiration()); | ||||||
|  |             userReopsitory.save(user); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExcep | |||||||
| 
 | 
 | ||||||
| @ControllerAdvice | @ControllerAdvice | ||||||
| public class RestExceptionHandler extends ResponseEntityExceptionHandler implements Serializable { | public class RestExceptionHandler extends ResponseEntityExceptionHandler implements Serializable { | ||||||
|  | 
 | ||||||
|     private static final long serialVersionUID = -3861125729653781371L; |     private static final long serialVersionUID = -3861125729653781371L; | ||||||
| 
 | 
 | ||||||
|     public RestExceptionHandler() { |     public RestExceptionHandler() { | ||||||
| @ -36,5 +37,4 @@ public class RestExceptionHandler extends ResponseEntityExceptionHandler impleme | |||||||
|         String response = "Error Occurred : " + ex.getMessage(); |         String response = "Error Occurred : " + ex.getMessage(); | ||||||
|         return handleExceptionInternal(ex, response, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request); |         return handleExceptionInternal(ex, response, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
| } | } | ||||||
| @ -0,0 +1,77 @@ | |||||||
|  | package org.baeldung.web.schedule; | ||||||
|  | 
 | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.text.ParseException; | ||||||
|  | import java.util.Arrays; | ||||||
|  | import java.util.Date; | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
|  | import org.baeldung.persistence.dao.PostRepository; | ||||||
|  | import org.baeldung.persistence.model.Post; | ||||||
|  | import org.baeldung.persistence.model.User; | ||||||
|  | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.scheduling.annotation.EnableScheduling; | ||||||
|  | import org.springframework.scheduling.annotation.Scheduled; | ||||||
|  | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||||||
|  | import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||||||
|  | import org.springframework.security.core.context.SecurityContextHolder; | ||||||
|  | import org.springframework.security.oauth2.client.OAuth2RestTemplate; | ||||||
|  | import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; | ||||||
|  | import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken; | ||||||
|  | import org.springframework.util.LinkedMultiValueMap; | ||||||
|  | import org.springframework.util.MultiValueMap; | ||||||
|  | 
 | ||||||
|  | import com.fasterxml.jackson.core.JsonProcessingException; | ||||||
|  | import com.fasterxml.jackson.databind.JsonNode; | ||||||
|  | 
 | ||||||
|  | @EnableScheduling | ||||||
|  | public class ScheduledTasks { | ||||||
|  | 
 | ||||||
|  |     private OAuth2RestTemplate redditRestTemplate; | ||||||
|  | 
 | ||||||
|  |     @Autowired | ||||||
|  |     private PostRepository postReopsitory; | ||||||
|  | 
 | ||||||
|  |     @Scheduled(fixedRate = 5 * 60 * 1000) | ||||||
|  |     public void reportCurrentTime() throws JsonProcessingException, IOException, ParseException { | ||||||
|  |         List<Post> posts = postReopsitory.findBySubmissionDateBefore(new Date()); | ||||||
|  |         System.out.println(posts.size()); | ||||||
|  |         for (Post post : posts) { | ||||||
|  |             if (post.isSent()) | ||||||
|  |                 continue; | ||||||
|  |             User user = post.getUser(); | ||||||
|  |             DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(user.getAccessToken()); | ||||||
|  |             token.setRefreshToken(new DefaultOAuth2RefreshToken((user.getRefreshToken()))); | ||||||
|  |             token.setExpiration(user.getTokenExpiration()); | ||||||
|  |             redditRestTemplate.getOAuth2ClientContext().setAccessToken(token); | ||||||
|  |             // | ||||||
|  |             UsernamePasswordAuthenticationToken userAuthToken = new UsernamePasswordAuthenticationToken(user.getUsername(), token.getValue(), Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"))); | ||||||
|  |             SecurityContextHolder.getContext().setAuthentication(userAuthToken); | ||||||
|  |             // | ||||||
|  | 
 | ||||||
|  |             MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>(); | ||||||
|  |             param.add("api_type", "json"); | ||||||
|  |             param.add("kind", "link"); | ||||||
|  |             param.add("resubmit", "true"); | ||||||
|  |             param.add("sendreplies", "false"); | ||||||
|  |             param.add("then", "comments"); | ||||||
|  |             param.add("title", post.getTitle()); | ||||||
|  |             param.add("sr", post.getSubreddit()); | ||||||
|  |             param.add("url", post.getUrl()); | ||||||
|  | 
 | ||||||
|  |             JsonNode node = redditRestTemplate.postForObject("https://oauth.reddit.com/api/submit", param, JsonNode.class); | ||||||
|  |             JsonNode errorNode = node.get("json").get("errors").get(0); | ||||||
|  |             if (errorNode == null) { | ||||||
|  |                 post.setSent(true); | ||||||
|  |                 postReopsitory.save(post); | ||||||
|  |             } else { | ||||||
|  |                 post.setSubmissionResponse(errorNode.toString()); | ||||||
|  |                 postReopsitory.save(post); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void setRedditRestTemplate(OAuth2RestTemplate redditRestTemplate) { | ||||||
|  |         this.redditRestTemplate = redditRestTemplate; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,10 @@ | |||||||
|  | ################### DataSource Configuration ########################## | ||||||
|  | jdbc.driverClassName=com.mysql.jdbc.Driver | ||||||
|  | jdbc.url=jdbc:mysql://localhost:3306/oauth_reddit?createDatabaseIfNotExist=true | ||||||
|  | jdbc.user=tutorialuser | ||||||
|  | jdbc.pass=tutorialmy5ql | ||||||
|  | init-db=false | ||||||
|  | ################### Hibernate Configuration ########################## | ||||||
|  | hibernate.dialect=org.hibernate.dialect.MySQLDialect | ||||||
|  | hibernate.show_sql=false | ||||||
|  | hibernate.hbm2ddl.auto=create-drop | ||||||
							
								
								
									
										54
									
								
								spring-security-oauth/src/main/webapp/WEB-INF/jsp/postListView.jsp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										54
									
								
								spring-security-oauth/src/main/webapp/WEB-INF/jsp/postListView.jsp
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> | ||||||
|  | <html xmlns="http://www.w3.org/1999/xhtml"> | ||||||
|  | <head> | ||||||
|  | 
 | ||||||
|  | <title>Spring Security OAuth</title> | ||||||
|  | <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> | ||||||
|  | 
 | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  | <nav class="navbar navbar-inverse"> | ||||||
|  |   <div class="container-fluid"> | ||||||
|  |     <!-- Brand and toggle get grouped for better mobile display --> | ||||||
|  |     <div class="navbar-header"> | ||||||
|  |       <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> | ||||||
|  |         <span class="sr-only">Toggle navigation</span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |       </button> | ||||||
|  |       <a class="navbar-brand" href="info">Spring Security OAuth</a> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <!-- Collect the nav links, forms, and other content for toggling --> | ||||||
|  |     <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> | ||||||
|  |       <ul class="nav navbar-nav"> | ||||||
|  |         <li class="active"><a href="#">My Scheduled Posts</a></li> | ||||||
|  |         <li><a href="post">Submit Post</a></li> | ||||||
|  |         <li><a href="postSchedule">Schedule Post</a></li> | ||||||
|  |       </ul> | ||||||
|  |        | ||||||
|  |     </div><!-- /.navbar-collapse --> | ||||||
|  |   </div><!-- /.container-fluid --> | ||||||
|  | </nav> | ||||||
|  | <div class="container"> | ||||||
|  | <h1>My Scheduled Posts</h1> | ||||||
|  | <table class="table table-bordered"> | ||||||
|  | <c:forEach var="post" items="${posts}" > | ||||||
|  | <thead> | ||||||
|  | <tr> | ||||||
|  | <th>Post title</th> | ||||||
|  | <th>Submission Date</th> | ||||||
|  | <th>Notes</th> | ||||||
|  | </tr> | ||||||
|  | </thead> | ||||||
|  |     <tr <c:if test="${post.isSent()}"> class="success"</c:if>> | ||||||
|  |         <td><c:out value="${post.getTitle()}"/></td> | ||||||
|  |         <td><c:out value="${post.getSubmissionDate()}"/></td> | ||||||
|  |         <td><c:out value="${post.getSubmissionResponse()}"/></td> | ||||||
|  |     </tr> | ||||||
|  | </c:forEach> | ||||||
|  | </table> | ||||||
|  | </div> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
| @ -7,12 +7,34 @@ | |||||||
| 
 | 
 | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
|  | <nav class="navbar navbar-inverse"> | ||||||
|  |   <div class="container-fluid"> | ||||||
|  |     <!-- Brand and toggle get grouped for better mobile display --> | ||||||
|  |     <div class="navbar-header"> | ||||||
|  |       <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> | ||||||
|  |         <span class="sr-only">Toggle navigation</span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |       </button> | ||||||
|  |       <a class="navbar-brand" href="#">Spring Security OAuth</a> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <!-- Collect the nav links, forms, and other content for toggling --> | ||||||
|  |     <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> | ||||||
|  |       <ul class="nav navbar-nav"> | ||||||
|  |         <li><a href="posts">My Scheduled Posts</a></li> | ||||||
|  |         <li><a href="post">Submit Post</a></li> | ||||||
|  |         <li><a href="postSchedule">Schedule Post</a></li> | ||||||
|  |       </ul> | ||||||
|  |        | ||||||
|  |     </div><!-- /.navbar-collapse --> | ||||||
|  |   </div><!-- /.container-fluid --> | ||||||
|  | </nav> | ||||||
| <div class="container"> | <div class="container"> | ||||||
| <c:choose> | <c:choose> | ||||||
|     <c:when test="${info != null}"> |     <c:when test="${info != null}"> | ||||||
|         <h1>Your Reddit Info</h1> |         <h1>Welcome, <small>${info}</small></h1> | ||||||
|         <b>Your reddit username is </b>${info} |  | ||||||
|         <br><br><br> |  | ||||||
|         <a href="post" class="btn btn-primary">Submit to Reddit</a> |         <a href="post" class="btn btn-primary">Submit to Reddit</a> | ||||||
|     </c:when> |     </c:when> | ||||||
|     <c:otherwise>  |     <c:otherwise>  | ||||||
|  | |||||||
							
								
								
									
										71
									
								
								spring-security-oauth/src/main/webapp/WEB-INF/jsp/schedulePostForm.jsp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										71
									
								
								spring-security-oauth/src/main/webapp/WEB-INF/jsp/schedulePostForm.jsp
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,71 @@ | |||||||
|  | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> | ||||||
|  | <html xmlns="http://www.w3.org/1999/xhtml"> | ||||||
|  | <head> | ||||||
|  | 
 | ||||||
|  | <title>Spring Security OAuth</title> | ||||||
|  | <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" />"> | ||||||
|  | <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> | ||||||
|  | 
 | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  | <nav class="navbar navbar-inverse"> | ||||||
|  |   <div class="container-fluid"> | ||||||
|  |     <!-- Brand and toggle get grouped for better mobile display --> | ||||||
|  |     <div class="navbar-header"> | ||||||
|  |       <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> | ||||||
|  |         <span class="sr-only">Toggle navigation</span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |       </button> | ||||||
|  |       <a class="navbar-brand" href="#">Spring Security OAuth</a> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <!-- Collect the nav links, forms, and other content for toggling --> | ||||||
|  |     <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> | ||||||
|  |       <ul class="nav navbar-nav"> | ||||||
|  |         <li><a href="posts">My Scheduled Posts</a></li> | ||||||
|  |         <li><a href="post">Submit Post</a></li> | ||||||
|  |         <li class="active"><a href="#">Schedule Post</a></li> | ||||||
|  |       </ul> | ||||||
|  |        | ||||||
|  |     </div><!-- /.navbar-collapse --> | ||||||
|  |   </div><!-- /.container-fluid --> | ||||||
|  | </nav> | ||||||
|  | <div class="container"> | ||||||
|  | <h1>Schedule Post</h1> | ||||||
|  | <form action="schedule" method="post"> | ||||||
|  | <div class="row"> | ||||||
|  | <div class="form-group"> | ||||||
|  |     <label class="col-sm-3">Title</label> | ||||||
|  |     <span class="col-sm-9"><input name="title" placeholder="title" class="form-control" /></span> | ||||||
|  | </div> | ||||||
|  | <br><br> | ||||||
|  | <div class="form-group"> | ||||||
|  |     <label class="col-sm-3">Url</label> | ||||||
|  |     <span class="col-sm-9"><input name="url" placeholder="url" class="form-control" /></span> | ||||||
|  | </div> | ||||||
|  | <br><br>   | ||||||
|  | <div class="form-group"> | ||||||
|  |     <label class="col-sm-3">Subreddit</label> | ||||||
|  |     <span class="col-sm-9"><input name="sr" placeholder="Subreddit" class="form-control" /></span> | ||||||
|  | </div> | ||||||
|  | <br><br> | ||||||
|  | 
 | ||||||
|  | <label class="col-sm-3">Submission Date</label> | ||||||
|  | <span class="col-sm-9"><input type="text" name="date" class="form-control"></span> | ||||||
|  |     <script type="text/javascript"> | ||||||
|  |         $(function(){ | ||||||
|  |             $('*[name=date]').appendDtpicker({"inline": true}); | ||||||
|  |         }); | ||||||
|  |     </script> | ||||||
|  | 
 | ||||||
|  |     <br><br> | ||||||
|  |     <button type="submit" class="btn btn-primary">Post</button> | ||||||
|  |    </div> | ||||||
|  | </form> | ||||||
|  | </div> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
| @ -7,6 +7,30 @@ | |||||||
| 
 | 
 | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
|  | <nav class="navbar navbar-inverse"> | ||||||
|  |   <div class="container-fluid"> | ||||||
|  |     <!-- Brand and toggle get grouped for better mobile display --> | ||||||
|  |     <div class="navbar-header"> | ||||||
|  |       <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> | ||||||
|  |         <span class="sr-only">Toggle navigation</span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |       </button> | ||||||
|  |       <a class="navbar-brand" href="#">Spring Security OAuth</a> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <!-- Collect the nav links, forms, and other content for toggling --> | ||||||
|  |     <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> | ||||||
|  |       <ul class="nav navbar-nav"> | ||||||
|  |         <li><a href="posts">My Scheduled Posts</a></li> | ||||||
|  |         <li class="active"><a href="#">Submit Post</a></li> | ||||||
|  |         <li><a href="postSchedule">Schedule Post</a></li> | ||||||
|  |       </ul> | ||||||
|  |        | ||||||
|  |     </div><!-- /.navbar-collapse --> | ||||||
|  |   </div><!-- /.container-fluid --> | ||||||
|  | </nav> | ||||||
| <div class="container"> | <div class="container"> | ||||||
| <h1>Submit to Reddit</h1> | <h1>Submit to Reddit</h1> | ||||||
| <form action="submit" method="post"> | <form action="submit" method="post"> | ||||||
|  | |||||||
| @ -7,9 +7,32 @@ | |||||||
| 
 | 
 | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
|  | <nav class="navbar navbar-inverse"> | ||||||
|  |   <div class="container-fluid"> | ||||||
|  |     <!-- Brand and toggle get grouped for better mobile display --> | ||||||
|  |     <div class="navbar-header"> | ||||||
|  |       <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> | ||||||
|  |         <span class="sr-only">Toggle navigation</span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |         <span class="icon-bar"></span> | ||||||
|  |       </button> | ||||||
|  |       <a class="navbar-brand" href="#">Spring Security OAuth</a> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <!-- Collect the nav links, forms, and other content for toggling --> | ||||||
|  |     <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> | ||||||
|  |       <ul class="nav navbar-nav"> | ||||||
|  |         <li><a href="posts">My Scheduled Posts</a></li> | ||||||
|  |         <li><a href="post">Submit Post</a></li> | ||||||
|  |         <li><a href="postSchedule">Schedule Post</a></li> | ||||||
|  |       </ul> | ||||||
|  |        | ||||||
|  |     </div><!-- /.navbar-collapse --> | ||||||
|  |   </div><!-- /.container-fluid --> | ||||||
|  | </nav> | ||||||
| <div class="container"> | <div class="container"> | ||||||
| <h1>${msg}</h1> | <h1>${msg}</h1> | ||||||
| <a href="post" class="btn btn-primary">Submit another link to Reddit</a> |  | ||||||
| </div> | </div> | ||||||
| </body> | </body> | ||||||
| </html> | </html> | ||||||
| @ -0,0 +1,332 @@ | |||||||
|  | /** | ||||||
|  |  * Style-sheet for dtpicker | ||||||
|  |  * https://github.com/mugifly/jquery-simple-datetimepicker | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | .datepicker { | ||||||
|  |     position: relative; | ||||||
|  |     display: inline-block; | ||||||
|  |     font: 15px/1.5 "Helvetica Neue", mplus-2c, Helvetica, Arial, "Hiragino Kaku Gothic Pro", Meiryo, sans-serif; | ||||||
|  |     font-weight: 300; | ||||||
|  |     border: 1px solid #dfdfdf; | ||||||
|  |     border-radius: 3px; | ||||||
|  |         -webkit-border-radius: 3px;   | ||||||
|  |         -moz-border-radius: 3px; | ||||||
|  |     box-shadow: 0.5px 0.5px 0px #c8c8c8; | ||||||
|  |         -webkit-box-shadow: 0.5px 0.5px 3px #eeeeee; | ||||||
|  |         -moz-box-shadow: 0.5px 0.5px 3px #eeeeee; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * datepicker_header | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_header{ | ||||||
|  |     padding-top:    2px; | ||||||
|  |     padding-bottom: 2px; | ||||||
|  |     padding-left:   5px; | ||||||
|  |     padding-right: 5px; | ||||||
|  |     background-color:   #eeeeee; | ||||||
|  |     color: #3f3f3f; | ||||||
|  |     text-align: center; | ||||||
|  |     font-size: 9pt; | ||||||
|  |     font-weight: bold; | ||||||
|  |     user-select: none; | ||||||
|  |         -webkit-user-select: none; | ||||||
|  |         -moz-user-select: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_header > a { | ||||||
|  |     user-select: none; | ||||||
|  |         -webkit-user-select: none; | ||||||
|  |         -moz-user-select: none; | ||||||
|  |     cursor: pointer; | ||||||
|  |     color: #3b7796; | ||||||
|  |     padding: 3px 16px; | ||||||
|  |     font-size: 20px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_header > a:hover { | ||||||
|  |     color: #303030; | ||||||
|  |     background-color:   #c8c8c8; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_header > a:active { | ||||||
|  |     color: #ffffff; | ||||||
|  |     background-color:   #808080; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_header > span { | ||||||
|  |     margin-left: 20px; | ||||||
|  |     margin-right: 20px; | ||||||
|  |     user-select: none; | ||||||
|  |         -webkit-user-select: none; | ||||||
|  |         -moz-user-select: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_header > .icon-home { | ||||||
|  |     position:   absolute; | ||||||
|  |     display:        block; | ||||||
|  |     width:      16px; | ||||||
|  |     height:     16px; | ||||||
|  |     vertical-align: middle; | ||||||
|  |     padding: 8px; | ||||||
|  |     top: 0; | ||||||
|  |     left: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_header > .icon-close { | ||||||
|  |     position: absolute; | ||||||
|  |     display: block; | ||||||
|  |     width: 16px; | ||||||
|  |     height: 16px; | ||||||
|  |     vertical-align: middle; | ||||||
|  |     padding: 8px; | ||||||
|  |     top: 0; | ||||||
|  |     right: 0; | ||||||
|  | } | ||||||
|  | .datepicker > .datepicker_header > .icon-home > div { | ||||||
|  |     width: 16px; | ||||||
|  |     height: 16px; | ||||||
|  |     background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAYAAAB3AH1ZAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjExR/NCNwAAAepJREFUSEudk71Kw2AUhnVxVnCyN1EcCjXUujmKF1AoRRCcNU4OIg5dtXF36tAb6B107qjg0iC4STt0EdTnTU8+v5rGNh54aM7Pe/J+abLmR3AZ7QeXD0+WZkI9zViaiXq9vg+5evU0Y+ksgjCqsrgPA2jBOzQs91FNPc0o70vLQtGHAbTgHRqW+6imnmaUS1PVqWL4SgijJr8juHK1H1Qb2Uxai1kivowmjODKq6Wopp5m0losA95Noh43ONXi+XpCPOsx49W9ZaIHp+CbSlFNPc24+i8DxfGX/YeZgTDqQrsgXc9AF9oFkcY9gQN7J3MjuOhs2WUS0ngGluqZmdNLs7IBTltmZsrvsZUKGaBfhik4vTSw3EBwfrdBfwhvwcX9tpVXNkBvA4bwBk4vDSw2UD27WbdLnf42mQmjIysl8ZeBWq3m9PRubWZOL43qGQOccpf8lc9tj5tWuP7g+tHaLqSR1pY7Pde78Ap7UIEPyOip5RgIOzvkzzCGF4gxtWltF9LAIgM78AxjeAF9/xk9tfy/gBuWOLVMfPIUDq08F3kGFOQlkIlPWKiXBvJfQl6+EjdvWJqJvwwoqMlErl4aabVowmmvbWEBEs2EJeLaFhZBmone8hMtAj2JIkykZYmQifRprAqa+sk3UgBXF8VWaxMAAAAASUVORK5CYII=); | ||||||
|  | } | ||||||
|  | .datepicker > .datepicker_header > .icon-close> div { | ||||||
|  |     width: 16px; | ||||||
|  |     height: 16px; | ||||||
|  |     background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAYAAAB3AH1ZAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjExR/NCNwAAAfhJREFUSEvNVEtKA0EUHHBhsvZ7D0HF4PRMnERxI+hCQ4xuBE/g5yLRA+il/CCIXkDElRGt6qkZO91j1JUW1KJf1XvVn2Gif4XGUX986aRf0zIANXq0DBBv7Iwvb+192U+NHi2H0Tg+O2ycnN2DV+CcyiVQW5B2T6/KJUxr7dAkySN4bVbaiyqXMM1s3mr0wKtyDgytgQ/gu/gKbkumvgsOpJH0lieNN3s1O9iYd/EN657kyJikZ2uFDi97JDOgX8fAWyeg2MQO6IeT8Pbrao+Wtw/qJklvywBnE5ZuOAkve9SeA0PnQT+Ia7/2BoZXnK0uBkH5Oqxl4RNZYHAH9ANdMnxP9gA42X5FoMuhp6kEAnjlDPpVeIERm0B4+m0/N9AFq26Bta5sX0JvPvDCycFPTs+PbtQTUOvIHgAhu+DoJ4BH9mFgcAzyy3cDee3+c9ATq60ErjfDcP/kDPQ3hJtIM7XlwMAJ8FoBBe2bi/4m6J1QO34yrWlc740XxDffr/wm6EWP2u0GJsEnDS/DJVPvge7T0DspmT+aGQx+cUKGPriKTcCbzEhGwOn5GAZegM/gHdiSVAK1VBo9F+yRFJn2+hgGXtrBSXpn0mbQb5orbavl4ZfskZRDm5gGp1QKQM16nPACdhNpczZur3+ezAM1eoLwv0MUfQBkquZISGUUwgAAAABJRU5ErkJggg==); | ||||||
|  | } | ||||||
|  | .datepicker > .datepicker_header > a:hover > div, .datepicker > .datepicker_header > a:hover > div  { | ||||||
|  |     background-position: -16px 0px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * datepicker_inner_container  | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container { | ||||||
|  |     margin: -2px 0px -2px 0px; | ||||||
|  |     background-color: #d2d2d2; | ||||||
|  |     border: 1px solid #c8c8c8; | ||||||
|  |     border-radius: 3px; | ||||||
|  |         -webkit-border-radius: 3px;   | ||||||
|  |         -moz-border-radius: 3px; | ||||||
|  | 
 | ||||||
|  |     box-shadow: 0.5px 0px 3px #c8c8c8; | ||||||
|  |         -webkit-box-shadow: 0.5px 0px 3px #c8c8c8; | ||||||
|  |         -moz-box-shadow: 0.5px 0px 3px #c8c8c8; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container:after { | ||||||
|  |     content: "."; | ||||||
|  |     display: block; | ||||||
|  |     height: 0; | ||||||
|  |     clear: both; | ||||||
|  |     visibility: hidden; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * datepicker_inner_container > datepicker_calendar | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_calendar { | ||||||
|  |     float: left; | ||||||
|  |     width: 18.3em; | ||||||
|  |      | ||||||
|  |     margin-top: -0.5px; | ||||||
|  |     margin-left: -1px; | ||||||
|  |     margin-bottom: -2px; | ||||||
|  |      | ||||||
|  |     background-color:   #ffffff; | ||||||
|  |     border: 1px solid #c8c8c8; | ||||||
|  |      | ||||||
|  |     border-top:none; | ||||||
|  |     border-top-left-radius: 3px; | ||||||
|  |     border-bottom-left-radius: 3px; | ||||||
|  |         -webkit-border-top-left-radius: 3px; | ||||||
|  |         -webkit-border-bottom-left-radius: 3px; | ||||||
|  |         -moz-border-radius-topleft:     3px; | ||||||
|  |         -moz-border-radius-bottomleft:  3px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_calendar > table { | ||||||
|  |     padding: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * datepicker_inner_container > datepicker_calendar > datepicker_table > tbody > tr > th (WDay-cell) | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > th { | ||||||
|  |     color:  #646464; | ||||||
|  |     width: 18px; | ||||||
|  |     font-size: small; | ||||||
|  |     font-weight: normal; | ||||||
|  |     text-align:center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * datepicker_inner_container > datepicker_calendar > datepicker_table > tbody > tr > td (Day-cell) | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td { | ||||||
|  |     color:  #000000; | ||||||
|  |     font-size: small; | ||||||
|  |     text-align:center; | ||||||
|  |      | ||||||
|  |     user-select: none; | ||||||
|  |         -webkit-user-select: none; | ||||||
|  |         -moz-user-select: none; | ||||||
|  |     cursor: pointer; | ||||||
|  |     padding: 10px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.today { | ||||||
|  |     border-bottom: #bfbfbf solid 2px; | ||||||
|  |     margin-bottom: -2px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.wday_sat { | ||||||
|  |     color:  #0044aa; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.wday_sun { | ||||||
|  |     color:  #e13b00; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.day_another_month { | ||||||
|  |     color:  #cccccc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.day_in_past { | ||||||
|  |     cursor: default; | ||||||
|  |     color: #cccccc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.day_in_unallowed { | ||||||
|  |     cursor: default; | ||||||
|  |     color: #cccccc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.out_of_range { | ||||||
|  |     cursor: default; | ||||||
|  |     color: #cccccc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.active { | ||||||
|  |     color: #ffffff; | ||||||
|  |     background-color:   #808080; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_calendar > .datepicker_table > tbody > tr > td.hover { | ||||||
|  |     color: #000000; | ||||||
|  |     background-color:   #c8c8c8; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |  * datepicker_inner_container > datepicker_timelist | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_timelist { | ||||||
|  |     float: left; | ||||||
|  |      | ||||||
|  |     margin-top: -0.5px; | ||||||
|  |     padding: 5px 0px; | ||||||
|  |      | ||||||
|  |     overflow: auto; | ||||||
|  |     overflow-x: hidden;  | ||||||
|  |      | ||||||
|  |     background-color:   #ffffff; | ||||||
|  |      | ||||||
|  |     border-top-right-radius: 3px; | ||||||
|  |     border-bottom-right-radius: 3px; | ||||||
|  |     -webkit-border-top-right-radius:    3px; | ||||||
|  |     -webkit-border-bottom-right-radius: 3px; | ||||||
|  |     -moz-border-radius-topright:        3px; | ||||||
|  |     -moz-border-radius-bottomright: 3px; | ||||||
|  |     text-align: right; | ||||||
|  |     width: 4.9em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_timelist::after { | ||||||
|  |     content: "."; | ||||||
|  |     display: block; | ||||||
|  |     height: 0; | ||||||
|  |     clear: both; | ||||||
|  |     visibility: hidden; | ||||||
|  | } | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_timelist::-webkit-scrollbar { | ||||||
|  |     overflow: hidden; | ||||||
|  |     width: 6px; | ||||||
|  |     background: #fafafa; | ||||||
|  |      | ||||||
|  |     border-top-right-radius: 3px; | ||||||
|  |     border-bottom-right-radius: 3px; | ||||||
|  |     -webkit-border-top-right-radius:    3px; | ||||||
|  |     -webkit-border-bottom-right-radius: 3px; | ||||||
|  |     -moz-border-radius-topright:        3px; | ||||||
|  |     -moz-border-radius-bottomright: 3px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_timelist::-webkit-scrollbar:horizontal { | ||||||
|  |     height: 1px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_timelist::-webkit-scrollbar-button { | ||||||
|  |     display: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_timelist::-webkit-scrollbar-piece { | ||||||
|  |     background: #eee; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_timelist::-webkit-scrollbar-piece:start { | ||||||
|  |     background: #eee; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_timelist::-webkit-scrollbar-thumb { | ||||||
|  |     background: #aaaaaa; | ||||||
|  |     border-radius: 3px; | ||||||
|  |         -webkit-border-radius: 3px;   | ||||||
|  |         -moz-border-radius: 3px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_timelist::-webkit-scrollbar-corner { | ||||||
|  |     background: #333; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_timelist > div.timelist_item { | ||||||
|  |     padding-top:   5px; | ||||||
|  |     padding-bottom:5px; | ||||||
|  |     padding-left:  7px; | ||||||
|  |     padding-right: 7px; | ||||||
|  |     margin-top: 5px; | ||||||
|  |     margin-bottom: 2px; | ||||||
|  |     font-size: small; | ||||||
|  |      | ||||||
|  |     user-select: none; | ||||||
|  |         -webkit-user-select: none; | ||||||
|  |         -moz-user-select: none; | ||||||
|  |     cursor: pointer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_timelist > div.timelist_item.time_in_past { | ||||||
|  |     cursor: default; | ||||||
|  |     color: #cccccc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_timelist > div.timelist_item.out_of_range { | ||||||
|  |     cursor: default; | ||||||
|  |     color: #cccccc; | ||||||
|  | } | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_timelist > div.timelist_item.active { | ||||||
|  |     color: #ffffff; | ||||||
|  |     background-color:   #808080; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .datepicker > .datepicker_inner_container > .datepicker_timelist > div.timelist_item.hover { | ||||||
|  |     color: #000000; | ||||||
|  |     background-color:   #c8c8c8; | ||||||
|  | } | ||||||
							
								
								
									
										1487
									
								
								spring-security-oauth/src/main/webapp/resources/datetime-picker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1487
									
								
								spring-security-oauth/src/main/webapp/resources/datetime-picker.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user