├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── create_admin_user.sh
├── pom.xml
└── src
├── main
├── java
│ └── me
│ │ └── woemler
│ │ └── springblog
│ │ ├── Application.java
│ │ ├── config
│ │ ├── AdminUserInitializer.java
│ │ ├── BlogPostInitializer.java
│ │ ├── DataSourceConfig.java
│ │ ├── EmbeddedDataSourceConfig.java
│ │ ├── SecurityConfig.java
│ │ └── WebAppConfig.java
│ │ ├── controllers
│ │ ├── AdminController.java
│ │ ├── BlogController.java
│ │ ├── CodeController.java
│ │ ├── LoginController.java
│ │ └── MediaController.java
│ │ ├── models
│ │ ├── BlogPost.java
│ │ └── User.java
│ │ ├── repositories
│ │ ├── BlogOperations.java
│ │ ├── BlogRepository.java
│ │ ├── BlogRepositoryImpl.java
│ │ ├── UserRepository.java
│ │ └── UserRepositoryImpl.java
│ │ └── services
│ │ └── GitHubService.java
├── resources
│ ├── application-dev.properties
│ ├── application-prd.properties
│ ├── application.properties
│ ├── data-source.properties
│ └── static
│ │ ├── css
│ │ ├── blog-style.css
│ │ ├── bootstrap-theme.min.css
│ │ ├── bootstrap.min.css
│ │ ├── jquery-ui.css
│ │ └── jquery.tagit.css
│ │ ├── fonts
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.svg
│ │ ├── glyphicons-halflings-regular.ttf
│ │ └── glyphicons-halflings-regular.woff
│ │ └── js
│ │ ├── bootstrap.min.js
│ │ ├── jquery-1.10.2.min.js
│ │ ├── jquery-ui.min.js
│ │ ├── less-1.5.0.min.js
│ │ └── tag-it.min.js
└── webapp
│ └── WEB-INF
│ ├── tags
│ ├── adminBlogEditHelp.tag
│ ├── adminSidebar.tag
│ ├── blogPostList.tag
│ ├── blogSidebar.tag
│ ├── footer.tag
│ └── header.tag
│ └── views
│ └── jsp
│ ├── about.jsp
│ ├── admin
│ ├── admin.jsp
│ ├── blogPostList.jsp
│ ├── editBlogPost.jsp
│ ├── editTag.jsp
│ └── tagList.jsp
│ ├── blogPost.jsp
│ ├── code.jsp
│ ├── home.jsp
│ ├── login.jsp
│ ├── media.jsp
│ ├── tagDetails.jsp
│ └── tagList.jsp
└── test
├── java
└── me
│ └── woemler
│ └── springblog
│ └── test
│ ├── RepositoryTests.java
│ └── config
│ ├── TestApplicationConfig.java
│ ├── TestDataSourceConfig.java
│ └── TestWebAppConfig.java
└── resources
└── test-blog.properties
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #################
2 | ## Spring-Blog
3 | #################
4 |
5 | target/
6 |
7 | #################
8 | ## IntelliJ
9 | #################
10 | *.iml
11 | .idea
12 |
13 | #################
14 | ## Eclipse
15 | #################
16 |
17 | *.pydevproject
18 | .project
19 | .metadata
20 | bin/
21 | tmp/
22 | *.tmp
23 | *.bak
24 | *.swp
25 | *~.nib
26 | local.properties
27 | .classpath
28 | .settings/
29 | .loadpath
30 |
31 | # External tool builders
32 | .externalToolBuilders/
33 |
34 | # Locally stored "Eclipse launch configurations"
35 | *.launch
36 |
37 | # CDT-specific
38 | .cproject
39 |
40 | # PDT-specific
41 | .buildpath
42 |
43 |
44 | #################
45 | ## Visual Studio
46 | #################
47 |
48 | ## Ignore Visual Studio temporary files, build results, and
49 | ## files generated by popular Visual Studio add-ons.
50 |
51 | # User-specific files
52 | *.suo
53 | *.user
54 | *.sln.docstates
55 |
56 | # Build results
57 |
58 | [Dd]ebug/
59 | [Rr]elease/
60 | x64/
61 | build/
62 | [Bb]in/
63 | [Oo]bj/
64 |
65 | # MSTest test Results
66 | [Tt]est[Rr]esult*/
67 | [Bb]uild[Ll]og.*
68 |
69 | *_i.c
70 | *_p.c
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.pch
75 | *.pdb
76 | *.pgc
77 | *.pgd
78 | *.rsp
79 | *.sbr
80 | *.tlb
81 | *.tli
82 | *.tlh
83 | *.tmp
84 | *.tmp_proj
85 | *.log
86 | *.vspscc
87 | *.vssscc
88 | .builds
89 | *.pidb
90 | *.log
91 | *.scc
92 |
93 | # Visual C++ cache files
94 | ipch/
95 | *.aps
96 | *.ncb
97 | *.opensdf
98 | *.sdf
99 | *.cachefile
100 |
101 | # Visual Studio profiler
102 | *.psess
103 | *.vsp
104 | *.vspx
105 |
106 | # Guidance Automation Toolkit
107 | *.gpState
108 |
109 | # ReSharper is a .NET coding add-in
110 | _ReSharper*/
111 | *.[Rr]e[Ss]harper
112 |
113 | # TeamCity is a build add-in
114 | _TeamCity*
115 |
116 | # DotCover is a Code Coverage Tool
117 | *.dotCover
118 |
119 | # NCrunch
120 | *.ncrunch*
121 | .*crunch*.local.xml
122 |
123 | # Installshield output folder
124 | [Ee]xpress/
125 |
126 | # DocProject is a documentation generator add-in
127 | DocProject/buildhelp/
128 | DocProject/Help/*.HxT
129 | DocProject/Help/*.HxC
130 | DocProject/Help/*.hhc
131 | DocProject/Help/*.hhk
132 | DocProject/Help/*.hhp
133 | DocProject/Help/Html2
134 | DocProject/Help/html
135 |
136 | # Click-Once directory
137 | publish/
138 |
139 | # Publish Web Output
140 | *.Publish.xml
141 | *.pubxml
142 |
143 | # NuGet Packages Directory
144 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
145 | #packages/
146 |
147 | # Windows Azure Build Output
148 | csx
149 | *.build.csdef
150 |
151 | # Windows Store app package directory
152 | AppPackages/
153 |
154 | # Others
155 | sql/
156 | *.Cache
157 | ClientBin/
158 | [Ss]tyle[Cc]op.*
159 | ~$*
160 | *~
161 | *.dbmdl
162 | *.[Pp]ublish.xml
163 | *.pfx
164 | *.publishsettings
165 |
166 | # RIA/Silverlight projects
167 | Generated_Code/
168 |
169 | # Backup & report files from converting an old project file to a newer
170 | # Visual Studio version. Backup files are not needed, because we have git ;-)
171 | _UpgradeReport_Files/
172 | Backup*/
173 | UpgradeLog*.XML
174 | UpgradeLog*.htm
175 |
176 | # SQL Server files
177 | App_Data/*.mdf
178 | App_Data/*.ldf
179 |
180 | #############
181 | ## Windows detritus
182 | #############
183 |
184 | # Windows image file caches
185 | Thumbs.db
186 | ehthumbs.db
187 |
188 | # Folder config file
189 | Desktop.ini
190 |
191 | # Recycle Bin used on file shares
192 | $RECYCLE.BIN/
193 |
194 | # Mac crap
195 | .DS_Store
196 |
197 |
198 | #############
199 | ## Python
200 | #############
201 |
202 | *.py[co]
203 |
204 | # Packages
205 | *.egg
206 | *.egg-info
207 | dist/
208 | build/
209 | eggs/
210 | parts/
211 | var/
212 | sdist/
213 | develop-eggs/
214 | .installed.cfg
215 |
216 | # Installer logs
217 | pip-log.txt
218 |
219 | # Unit test / coverage reports
220 | .coverage
221 | .tox
222 |
223 | #Translations
224 | *.mo
225 |
226 | #Mr Developer
227 | .mr.developer.cfg
228 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 William Oemler
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Spring-Blog
2 | ===========
3 |
4 | A generic blog application built using Spring Boot and MongoDB. A good example application for illustrating Spring Framework features, such as:
5 |
6 | - Spring MVC and JSP
7 | - Spring Data with MongoDB repositories.
8 | - Spring Security CSRF and CORS protections.
9 | - Simple content management and media sharing tools.
10 | - Testing and deployment with Spring Boot.
11 |
12 | ### Requirements
13 |
14 | - JDK 7+
15 | - Maven 2+
16 | - MongoDB 2+ (embedded database provided for testing)
17 |
18 | ### Quick Start
19 | - Clone this repository.
20 | - Make the appropriate changes to the properties files in `src/main/resources`.
21 | - Build the project and run tests by running:
22 | - `mvn clean install`
23 | - Run the application in development mode with an embedded database by running:
24 | - `mvn spring-boot:run` or `java -jar target/spring-blog.jar`
25 | - Run the application in production mode with a permanent database by running:
26 | - `java -jar target/spring-blog.jar --spring.profiles.active=prd`
27 |
28 | ### License
29 |
30 | This project is licensed under the terms of the MIT License.
31 |
32 | >The MIT License (MIT)
33 | >
34 | >Copyright (c) 2017 William Oemler
35 | >
36 | >Permission is hereby granted, free of charge, to any person obtaining a copy
37 | of this software and associated documentation files (the "Software"), to deal
38 | in the Software without restriction, including without limitation the rights
39 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
40 | copies of the Software, and to permit persons to whom the Software is
41 | furnished to do so, subject to the following conditions:
42 |
43 | >The above copyright notice and this permission notice shall be included in all
44 | copies or substantial portions of the Software.
45 |
46 | >THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
47 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
48 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
49 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
50 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
51 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
52 | SOFTWARE.
53 |
--------------------------------------------------------------------------------
/create_admin_user.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | read -p "Enter desired username: " user
3 | if [ -z "$user" ]; then
4 | echo "Username must not be empty!"
5 | break;
6 | fi
7 | read -s -p "Enter password: " pw
8 | if [ -z "$pw" ]; then
9 | echo "Password must not be empty!"
10 | break;
11 | fi
12 | echo ""
13 | read -s -p "Enter password again : " pw2
14 | if [ "$pw" != "$pw2" ]; then
15 | echo "Passwords do not match!"
16 | break;
17 | fi
18 | echo ""
19 | read -p "Enter email: " email
20 | if [ -z "$email" ]; then
21 | echo "Email must not be empty!"
22 | break;
23 | fi
24 | mvn -e -q -f pom.xml exec:java -Dexec.mainClass="me.woemler.springblog.security.CreateAdminUser" -Dexec.args="$user $pw $email"
25 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | me.woemler
6 | spring-blog
7 | 1.0-SNAPSHOT
8 | jar
9 |
10 | Spring Blog
11 |
12 |
13 | Athens-RELEASE
14 |
15 |
16 |
17 |
18 |
19 | io.spring.platform
20 | platform-bom
21 | ${spring.platform.version}
22 | pom
23 | import
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | org.springframework.boot
34 | spring-boot-starter-web
35 |
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-starter-tomcat
40 | provided
41 |
42 |
43 |
44 | org.apache.tomcat.embed
45 | tomcat-embed-jasper
46 | provided
47 |
48 |
49 |
50 | org.springframework.boot
51 | spring-boot-starter-logging
52 |
53 |
54 |
55 | org.springframework.boot
56 | spring-boot-starter-ws
57 |
58 |
59 |
60 | org.springframework.boot
61 | spring-boot-starter-actuator
62 |
63 |
64 |
65 | org.springframework.boot
66 | spring-boot-devtools
67 | true
68 |
69 |
70 |
71 | org.springframework.boot
72 | spring-boot-starter-security
73 |
74 |
75 |
76 | org.springframework.boot
77 | spring-boot-starter-hateoas
78 |
79 |
80 |
81 | org.springframework.data
82 | spring-data-mongodb
83 |
84 |
85 |
86 | de.flapdoodle.embed
87 | de.flapdoodle.embed.mongo
88 | 1.50.5
89 |
90 |
91 |
92 | cz.jirutka.spring
93 | embedmongo-spring
94 | 1.3.1
95 |
96 |
97 |
98 |
99 |
100 | com.fasterxml.jackson.core
101 | jackson-annotations
102 |
103 |
104 |
105 |
106 |
107 | javax.servlet
108 | jstl
109 |
110 |
111 |
112 | org.pegdown
113 | pegdown
114 | 1.4.1
115 |
116 |
117 |
118 | com.google.code.gson
119 | gson
120 |
121 |
122 |
123 |
124 |
125 | org.hamcrest
126 | hamcrest-all
127 | test
128 |
129 |
130 |
131 | junit
132 | junit
133 | test
134 |
135 |
136 | hamcrest-core
137 | org.hamcrest
138 |
139 |
140 |
141 |
142 |
143 | org.mockito
144 | mockito-core
145 | test
146 |
147 |
148 |
149 | org.springframework
150 | spring-test
151 | test
152 |
153 |
154 |
155 | com.jayway.jsonpath
156 | json-path
157 | 1.2.0
158 | test
159 |
160 |
161 |
162 | com.jayway.jsonpath
163 | json-path-assert
164 | 1.2.0
165 | test
166 |
167 |
168 |
169 |
170 |
171 | spring-blog
172 |
173 |
174 | maven-surefire-plugin
175 | 2.18.1
176 |
177 |
178 | **/*Tests.java
179 |
180 |
181 |
182 |
183 | org.springframework.boot
184 | spring-boot-maven-plugin
185 |
186 | me.woemler.springblog.Application
187 |
188 |
189 |
190 | org.apache.maven.plugins
191 | maven-compiler-plugin
192 |
193 | 1.7
194 | 1.7
195 |
196 |
197 |
198 |
199 |
200 |
201 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/Application.java:
--------------------------------------------------------------------------------
1 | package me.woemler.springblog;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.boot.builder.SpringApplicationBuilder;
6 | import org.springframework.boot.context.web.SpringBootServletInitializer;
7 |
8 | /**
9 | * @author woemler
10 | */
11 |
12 | @SpringBootApplication
13 | public class Application extends SpringBootServletInitializer {
14 |
15 | @Override
16 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application){
17 | return application.sources(Application.class);
18 | }
19 |
20 | public static void main(String[] args){
21 | SpringApplication.run(Application.class, args);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/config/AdminUserInitializer.java:
--------------------------------------------------------------------------------
1 | package me.woemler.springblog.config;
2 |
3 | import me.woemler.springblog.models.User;
4 | import me.woemler.springblog.repositories.UserRepository;
5 |
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.boot.CommandLineRunner;
10 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
11 | import org.springframework.stereotype.Component;
12 |
13 | import java.util.Arrays;
14 | import java.util.Date;
15 | import java.util.HashSet;
16 | import java.util.UUID;
17 |
18 | /**
19 | * @author woemler
20 | */
21 | @Component
22 | public class AdminUserInitializer implements CommandLineRunner {
23 |
24 | @Autowired private UserRepository userRepository;
25 |
26 | private static final Logger logger = LoggerFactory.getLogger(AdminUserInitializer.class);
27 |
28 | public void run(String[] args) throws Exception {
29 |
30 | BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
31 |
32 | if (userRepository.loadUserByUsername("admin") != null){
33 | logger.info(String.format("ERROR: User %s already exists!", "admin"));
34 | return;
35 | }
36 |
37 | String password = UUID.randomUUID().toString();
38 | User user = new User();
39 | user.setUsername("admin");
40 | user.setPassword(encoder.encode(password));
41 | user.setRegistrationDate(new Date());
42 | user.setRoles(new HashSet<>(Arrays.asList(User.ROLE_USER, User.ROLE_ADMIN)));
43 | userRepository.insert(user);
44 |
45 | logger.info(String.format("SUCCESS: Created admin user with username %s and password %s", "admin", password));
46 |
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/config/BlogPostInitializer.java:
--------------------------------------------------------------------------------
1 | package me.woemler.springblog.config;
2 |
3 | import org.pegdown.PegDownProcessor;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.CommandLineRunner;
8 | import org.springframework.core.env.Environment;
9 | import org.springframework.stereotype.Component;
10 |
11 | import java.util.Arrays;
12 | import java.util.Date;
13 | import java.util.HashSet;
14 |
15 | import me.woemler.springblog.models.BlogPost;
16 | import me.woemler.springblog.repositories.BlogRepository;
17 |
18 | /**
19 | * @author woemler
20 | */
21 | @Component
22 | public class BlogPostInitializer implements CommandLineRunner {
23 |
24 | @Autowired private Environment env;
25 | @Autowired private BlogRepository blogRepository;
26 |
27 | private static final Logger logger = LoggerFactory.getLogger(BlogPostInitializer.class);
28 |
29 | @Override
30 | public void run(String... strings) throws Exception {
31 | if (Arrays.asList(env.getActiveProfiles()).contains("dev")){
32 | String content = "Welcome to your new Spring Boot and MongoDB powered blog! If you are seeing this post, your application is currently in development mode. In this mode, the application uses an embedded MongoDB instance that will be created and destroyed with the application context. Any new posts you create will not persist beyond the life of the application. You can login the admin page of the blog application [here](/login). You can find the username and password for the default admin user in the application log:\n" +
33 | "\n" +
34 | " Created admin user with username admin and password dd191771-c9d6-4ddf-aa55-f3a7daabf32d\n" +
35 | "\n" +
36 | "\n" +
37 | "To switch to production mode, you can change the active profile in the `application.properties` file:\n" +
38 | "\n" +
39 | " spring.profiles.active=prd\n" +
40 | "\n" +
41 | "Or, you can set the profile from the command line when your start the application:\n" +
42 | "\n" +
43 | " java -jar blog.jar --spring.profiles.active=prd\n" +
44 | "\n" +
45 | "Be sure to update the properties in the `application.properties` and `application-prd.properties` files to suit your needs.";
46 |
47 | BlogPost post = new BlogPost();
48 | post.setEnableComments(false);
49 | post.setPostDate(new Date());
50 | post.setStatus(BlogPost.STATUS_ACTIVE);
51 | post.setSlug("welcome-to-your-new-blog");
52 | post.setTags(new HashSet<>(Arrays.asList("welcome")));
53 | post.setContent(content);
54 | post.setTitle("Welcome to your new blog!");
55 | blogRepository.insert(post);
56 | logger.info("Created default blog post.");
57 | } else {
58 | logger.info("Application is not in development mode, no default posts created.");
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/config/DataSourceConfig.java:
--------------------------------------------------------------------------------
1 | package me.woemler.springblog.config;
2 |
3 | import com.mongodb.Mongo;
4 | import com.mongodb.MongoClient;
5 | import com.mongodb.MongoCredential;
6 | import com.mongodb.ServerAddress;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.context.annotation.Bean;
9 | import org.springframework.context.annotation.ComponentScan;
10 | import org.springframework.context.annotation.Configuration;
11 | import org.springframework.context.annotation.Profile;
12 | import org.springframework.context.annotation.PropertySource;
13 | import org.springframework.core.env.Environment;
14 | import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
15 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | /**
21 | * @author woemler
22 | */
23 |
24 | @Configuration
25 | @Profile({ "prd" })
26 | @PropertySource({"classpath:data-source.properties"})
27 | @ComponentScan(basePackages = { "me.woemler.springblog.services" })
28 | @EnableMongoRepositories(basePackages = "me.woemler.springblog.repositories")
29 | public class DataSourceConfig extends AbstractMongoConfiguration {
30 |
31 | @Autowired private Environment env;
32 |
33 | @Override
34 | public String getDatabaseName(){
35 | return env.getRequiredProperty("mongo.name");
36 | }
37 |
38 | @Override
39 | @Bean
40 | public Mongo mongo() throws Exception {
41 | ServerAddress serverAddress = new ServerAddress(env.getRequiredProperty("mongo.host"));
42 | List credentials = new ArrayList<>();
43 | return new MongoClient(serverAddress, credentials);
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/config/EmbeddedDataSourceConfig.java:
--------------------------------------------------------------------------------
1 | package me.woemler.springblog.config;
2 |
3 | import com.mongodb.Mongo;
4 |
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.ComponentScan;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.context.annotation.Profile;
9 | import org.springframework.data.mongodb.core.MongoTemplate;
10 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
11 |
12 | import java.io.IOException;
13 |
14 | import cz.jirutka.spring.embedmongo.EmbeddedMongoBuilder;
15 |
16 | /**
17 | * @author woemler
18 | */
19 | @Configuration
20 | @Profile({ "dev", "default" })
21 | @ComponentScan(basePackages = { "me.woemler.springblog.services" })
22 | @EnableMongoRepositories(basePackages = "me.woemler.springblog.repositories")
23 | public class EmbeddedDataSourceConfig {
24 |
25 | @Bean(destroyMethod = "close")
26 | public Mongo mongo() throws IOException {
27 | return new EmbeddedMongoBuilder().build();
28 | }
29 |
30 | @Bean
31 | public MongoTemplate mongoTemplate(Mongo mongo){
32 | return new MongoTemplate(mongo, "blog");
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package me.woemler.springblog.config;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.beans.factory.annotation.Qualifier;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.context.annotation.Import;
7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
11 | import org.springframework.security.core.userdetails.UserDetailsService;
12 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
13 |
14 | import me.woemler.springblog.repositories.UserRepository;
15 |
16 | /**
17 | * @author woemler
18 | *
19 | */
20 |
21 | @Configuration
22 | @EnableWebSecurity
23 | @Import(DataSourceConfig.class)
24 | public class SecurityConfig extends WebSecurityConfigurerAdapter {
25 |
26 | @SuppressWarnings("SpringJavaAutowiringInspection")
27 | @Autowired
28 | private UserRepository userRepository;
29 |
30 | @Autowired
31 | public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
32 | auth
33 | .userDetailsService(userRepository)
34 | .passwordEncoder(new BCryptPasswordEncoder());
35 | }
36 |
37 | @Override
38 | protected void configure(HttpSecurity http) throws Exception {
39 | http
40 | .authorizeRequests()
41 | .antMatchers("/", "/blog/**", "/static/**", "/about", "/code", "/media", "/error").permitAll()
42 | .antMatchers("/admin/**").hasRole("ADMIN")
43 | .anyRequest().authenticated()
44 | .and()
45 | .formLogin()
46 | .loginPage("/login")
47 | .permitAll()
48 | .defaultSuccessUrl("/admin")
49 | .failureUrl("/login")
50 | .and()
51 | .logout()
52 | .logoutUrl("/logout")
53 | .logoutSuccessUrl("/login");
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/config/WebAppConfig.java:
--------------------------------------------------------------------------------
1 | package me.woemler.springblog.config;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.data.web.config.EnableSpringDataWebSupport;
5 | import org.springframework.web.servlet.config.annotation.CorsRegistry;
6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
7 |
8 | /**
9 | * @author woemler
10 | */
11 |
12 | @Configuration
13 | @EnableSpringDataWebSupport
14 | public class WebAppConfig extends WebMvcConfigurerAdapter {
15 |
16 | @Override
17 | public void addCorsMappings(CorsRegistry registry) {
18 | registry.addMapping("/api/**");
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/controllers/AdminController.java:
--------------------------------------------------------------------------------
1 |
2 | package me.woemler.springblog.controllers;
3 |
4 | import me.woemler.springblog.models.BlogPost;
5 | import me.woemler.springblog.repositories.BlogRepository;
6 | import org.pegdown.PegDownProcessor;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.stereotype.Controller;
11 | import org.springframework.ui.Model;
12 | import org.springframework.ui.ModelMap;
13 | import org.springframework.validation.BindingResult;
14 | import org.springframework.web.bind.annotation.*;
15 | import org.springframework.web.bind.support.SessionStatus;
16 |
17 | import javax.validation.Valid;
18 | import java.util.*;
19 |
20 | @Controller
21 | @RequestMapping("/admin")
22 | @SessionAttributes("blogPost")
23 | public class AdminController {
24 |
25 | private static Logger logger = LoggerFactory.getLogger(AdminController.class);
26 |
27 | @Autowired BlogRepository blogRepository;
28 |
29 | @ModelAttribute("blogPost")
30 | public BlogPost getBlogPost(){
31 | return new BlogPost();
32 | }
33 |
34 | //Admin Home
35 | @RequestMapping(value="")
36 | public String admin(ModelMap map){
37 | Long postCount = blogRepository.count();
38 | Integer tagCount = blogRepository.findAllTags().size();
39 | map.addAttribute("postCount", postCount);
40 | map.addAttribute("tagCount", tagCount);
41 | return "admin/admin";
42 | }
43 |
44 | //List Blog Posts
45 | @RequestMapping(value="/blog", method= RequestMethod.GET)
46 | public String blogAdmin(ModelMap map, SessionStatus status){
47 | status.setComplete();
48 | List postList = blogRepository.findAll();
49 | map.addAttribute("postList", postList);
50 | return "admin/blogPostList";
51 | }
52 |
53 | //Add new blog post
54 | @RequestMapping(value="/blog/new", method=RequestMethod.GET)
55 | public String newPost(ModelMap map){
56 | BlogPost blogPost = new BlogPost();
57 | map.addAttribute("blogPost", blogPost);
58 | return "admin/editBlogPost";
59 | }
60 |
61 | //Save new post
62 | @RequestMapping(value="/blog/new", method=RequestMethod.POST)
63 | public String addPost(@Valid @ModelAttribute BlogPost blogPost,
64 | BindingResult result,
65 | @RequestParam(value="tagString", defaultValue="") String tagString,
66 | @RequestParam(value="enablePegdown", required=false) boolean enablePegdown,
67 | Model model,
68 | SessionStatus status)
69 | {
70 | if (result.hasErrors()){
71 | return "admin/editBlogPost";
72 | }
73 | Set tagSet = new HashSet<>();
74 | for (String tag: tagString.split(",")){
75 | tag = tag.replaceAll("\\s+", "");
76 | if (tag != null && !tag.equals("")){
77 | tagSet.add(tag);
78 | }
79 | }
80 | blogPost.setEnablePegdown(enablePegdown);
81 | blogPost.setPostDate(new Date());
82 | blogPost.setTags(tagSet);
83 | blogRepository.save(blogPost);
84 | status.setComplete();
85 | return "redirect:/admin/blog";
86 | }
87 |
88 | //Edit existing blog post
89 | @RequestMapping(value="/blog/{id}", method=RequestMethod.GET)
90 | public String editPost(ModelMap map, @PathVariable("id") String postId){
91 | BlogPost blogPost = blogRepository.findOne(postId);
92 | map.addAttribute("blogPost", blogPost);
93 | Set tags = blogPost.getTags();
94 | String tagString = "";
95 | for (String tag: tags){
96 | tagString = tagString + " " + tag;
97 | }
98 | tagString = tagString.trim();
99 | map.addAttribute("tagString", tagString);
100 | return "admin/editBlogPost";
101 | }
102 |
103 | //Update post
104 | @RequestMapping(value="/blog/{id}", method=RequestMethod.POST)
105 | public String savePostChanges(
106 | @Valid @ModelAttribute BlogPost blogPost,
107 | BindingResult result,
108 | @RequestParam(value="tagString", defaultValue="") String tagString,
109 | @RequestParam("enablePegdown") boolean enablePegdown,
110 | Model model,
111 | SessionStatus status)
112 | {
113 | if (result.hasErrors()){
114 | return "admin/editBlogPost";
115 | }
116 | Set tagSet = new HashSet();
117 | for (String tag: tagString.split(",")){
118 | tag = tag.replaceAll("\\s+", "");
119 | if (!tag.equals("") && tag != null){
120 | tagSet.add(tag);
121 | }
122 | }
123 | if (enablePegdown){
124 | blogPost.setContent(new PegDownProcessor().markdownToHtml(blogPost.getContent()));
125 | }
126 | blogPost.setTags(tagSet);
127 | blogPost.setPostDate(new Date());
128 | blogRepository.save(blogPost);
129 | status.setComplete();
130 | return "redirect:/admin/blog";
131 | }
132 |
133 | //Delete blog post
134 | @RequestMapping(value="/blog/delete/{id}", method=RequestMethod.POST)
135 | public @ResponseBody String deleteBlogPost(@PathVariable("id") String id, SessionStatus status){
136 | blogRepository.delete(id);
137 | status.setComplete();
138 | return "The item was deleted succesfully";
139 | }
140 |
141 | //Cancel post edit
142 | @RequestMapping(value="/blog/cancel", method=RequestMethod.GET)
143 | public String cancelBlogEdit(SessionStatus status){
144 | status.setComplete();
145 | return "redirect:/admin/blog";
146 | }
147 |
148 | //Tag list
149 | @RequestMapping(value="/tags", method=RequestMethod.GET)
150 | public String adminTagList(ModelMap map, SessionStatus status){
151 | status.setComplete();
152 | List posts = blogRepository.findAll();
153 | Map tagCounts = new HashMap<>();
154 | for (BlogPost post: posts){
155 | for (String tag: post.getTags()){
156 | if (!tagCounts.containsKey(tag)){
157 | tagCounts.put(tag, 0);
158 | }
159 | Integer count = tagCounts.get(tag);
160 | tagCounts.put(tag, count + 1);
161 | }
162 | }
163 | map.addAttribute("tags", tagCounts.keySet());
164 | map.addAttribute("tagCounts", tagCounts);
165 | return "admin/tagList";
166 | }
167 |
168 | }
169 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/controllers/BlogController.java:
--------------------------------------------------------------------------------
1 |
2 | package me.woemler.springblog.controllers;
3 |
4 | import me.woemler.springblog.models.BlogPost;
5 | import me.woemler.springblog.repositories.BlogRepository;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.data.domain.Page;
10 | import org.springframework.data.domain.Pageable;
11 | import org.springframework.data.domain.Sort;
12 | import org.springframework.data.web.PageableDefault;
13 | import org.springframework.stereotype.Controller;
14 | import org.springframework.ui.ModelMap;
15 | import org.springframework.web.bind.annotation.PathVariable;
16 | import org.springframework.web.bind.annotation.RequestMapping;
17 | import org.springframework.web.bind.annotation.RequestMethod;
18 |
19 | import java.util.*;
20 |
21 | @Controller
22 | public class BlogController {
23 |
24 | private static Logger logger = LoggerFactory.getLogger(BlogController.class);
25 |
26 | @Autowired private BlogRepository blogRepository;
27 |
28 | @RequestMapping(value={"/", "", "/blog"}, method=RequestMethod.GET)
29 | public String home(
30 | @PageableDefault(size = 5, sort = {"postDate"}, direction = Sort.Direction.DESC) Pageable pageable,
31 | ModelMap map
32 | ) {
33 | Page posts = blogRepository.findByStatusOrderByPostDateAsc(BlogPost.STATUS_ACTIVE, pageable);
34 | List blogPosts = new ArrayList<>(posts.getContent());
35 | map.addAttribute("currentPage", pageable.getPageNumber() + 1);
36 | map.addAttribute("numPages", posts.getTotalPages());
37 | map.addAttribute("postCount", posts.getTotalElements());
38 | map.addAttribute("blogPosts", blogPosts);
39 | return "home";
40 | }
41 |
42 | @RequestMapping(value="/blog/{slug}")
43 | public String post(ModelMap map, @PathVariable("slug") String slug){
44 | BlogPost post = blogRepository.findBySlug(slug);
45 | map.addAttribute("requestedPost", post);
46 | map.addAttribute("nextPost", null);
47 | map.addAttribute("previousPost", null);
48 | return "blogPost";
49 | }
50 |
51 | @RequestMapping(value="/tags", method=RequestMethod.GET)
52 | public String listTags(ModelMap map){
53 | List tags = blogRepository.findAllTags();
54 | List posts = blogRepository.findAll();
55 | Map tagCounts = new HashMap<>();
56 | for (BlogPost post: posts){
57 | for (String tag: post.getTags()){
58 | if (!tagCounts.containsKey(tag)){
59 | tagCounts.put(tag, 0);
60 | }
61 | Integer count = tagCounts.get(tag);
62 | tagCounts.put(tag, count + 1);
63 | }
64 | }
65 | map.addAttribute("tags", tagCounts.keySet());
66 | map.addAttribute("tagCounts", tagCounts);
67 | return "tagList";
68 | }
69 |
70 | @RequestMapping(value="/tags/{tag}", method=RequestMethod.GET)
71 | public String tagDetails(ModelMap map, @PathVariable("tag") String tag){
72 | List blogPosts = blogRepository.findByTags(tag);
73 | map.addAttribute("tag", tag);
74 | map.addAttribute("blogPosts", blogPosts);
75 | return "tagDetails";
76 | }
77 |
78 | //About page
79 | @RequestMapping(value="/about")
80 | public String about(ModelMap map){
81 | return "about";
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/controllers/CodeController.java:
--------------------------------------------------------------------------------
1 |
2 | package me.woemler.springblog.controllers;
3 |
4 | import me.woemler.springblog.services.GitHubService;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.beans.factory.annotation.Value;
9 | import org.springframework.stereotype.Controller;
10 | import org.springframework.ui.ModelMap;
11 | import org.springframework.web.bind.annotation.RequestMapping;
12 | import org.springframework.web.bind.annotation.RequestMethod;
13 |
14 | import java.net.MalformedURLException;
15 | import java.util.List;
16 | import java.util.Map;
17 |
18 | @Controller
19 | public class CodeController {
20 |
21 | private static Logger logger = LoggerFactory.getLogger(CodeController.class);
22 |
23 | @Autowired
24 | private GitHubService ghService;
25 |
26 | @Value("${github.username}")
27 | String githubUsername;
28 |
29 | @RequestMapping(value="/code", method=RequestMethod.GET)
30 | public String codePage(ModelMap map)
31 | throws MalformedURLException, Exception
32 | {
33 | List
");
137 | String paragraph = paragraphs[0] + "";
138 | if (paragraphs.length>1){
139 | paragraph = paragraph + "More...
";
140 | }
141 | return paragraph;
142 | }
143 |
144 | public static final String STATUS_ACTIVE = "active";
145 | public static final String STATUS_INACTIVE = "inactive";
146 |
147 | @Override public String toString() {
148 | return "BlogPost{" +
149 | "postId='" + postId + '\'' +
150 | ", title='" + title + '\'' +
151 | ", slug='" + slug + '\'' +
152 | ", content='" + content + '\'' +
153 | ", postDate=" + postDate +
154 | ", status='" + status + '\'' +
155 | ", enableComments=" + enableComments +
156 | ", tags=" + tags +
157 | '}';
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/models/User.java:
--------------------------------------------------------------------------------
1 | package me.woemler.springblog.models;
2 |
3 | import org.springframework.data.annotation.Id;
4 | import org.springframework.data.annotation.PersistenceConstructor;
5 | import org.springframework.data.mongodb.core.index.Indexed;
6 | import org.springframework.data.mongodb.core.mapping.Document;
7 | import org.springframework.security.core.GrantedAuthority;
8 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
9 | import org.springframework.security.core.userdetails.UserDetails;
10 |
11 | import javax.validation.constraints.NotNull;
12 | import java.util.*;
13 |
14 | /**
15 | * @author woemler
16 | */
17 |
18 | @Document(collection = "users")
19 | public class User implements UserDetails {
20 |
21 | @Id private String userId;
22 | @NotNull @Indexed(unique = true) private String username;
23 | @NotNull private String password;
24 | @NotNull private String name;
25 | @NotNull private String email;
26 | @NotNull private Date registrationDate = new Date();
27 | private Set roles = new HashSet<>();
28 | private boolean accountNonExpired = true;
29 | private boolean accountNonLocked = true;
30 | private boolean enabled = true;
31 | private boolean credentialsNonExpired = true;
32 |
33 | public User() { }
34 |
35 | @PersistenceConstructor
36 | public User(String userId, String username, String password, String name, String email,
37 | Date registrationDate, Set roles, boolean accountNonExpired, boolean accountNonLocked,
38 | boolean enabled, boolean credentialsNonExpired) {
39 | this.userId = userId;
40 | this.username = username;
41 | this.password = password;
42 | this.name = name;
43 | this.email = email;
44 | this.registrationDate = registrationDate;
45 | this.roles = roles;
46 | this.accountNonExpired = accountNonExpired;
47 | this.accountNonLocked = accountNonLocked;
48 | this.enabled = enabled;
49 | this.credentialsNonExpired = credentialsNonExpired;
50 | }
51 |
52 | public String getUserId() {
53 | return userId;
54 | }
55 |
56 | public void setUserId(String userId) {
57 | this.userId = userId;
58 | }
59 |
60 | public String getUsername() {
61 | return username;
62 | }
63 |
64 | public void setUsername(String username) {
65 | this.username = username;
66 | }
67 |
68 | public String getPassword() {
69 | return password;
70 | }
71 |
72 | public void setPassword(String password) {
73 | this.password = password;
74 | }
75 |
76 | public String getName() {
77 | return name;
78 | }
79 |
80 | public void setName(String name) {
81 | this.name = name;
82 | }
83 |
84 | public String getEmail() {
85 | return email;
86 | }
87 |
88 | public void setEmail(String email) {
89 | this.email = email;
90 | }
91 |
92 | public Date getRegistrationDate() {
93 | return registrationDate;
94 | }
95 |
96 | public void setRegistrationDate(Date registrationDate) {
97 | this.registrationDate = registrationDate;
98 | }
99 |
100 | public Set getRoles() {
101 | return roles;
102 | }
103 |
104 | public void setRoles(Set roles) {
105 | this.roles = roles;
106 | }
107 |
108 | @Override
109 | public Collection extends GrantedAuthority> getAuthorities() {
110 | if (roles == null){
111 | return Collections.emptyList();
112 | }
113 | Set authorities = new HashSet<>();
114 | for (String role: roles){
115 | authorities.add(new SimpleGrantedAuthority(role));
116 | }
117 | return authorities;
118 | }
119 |
120 | @Override
121 | public boolean isAccountNonExpired() {
122 | return accountNonExpired;
123 | }
124 |
125 | @Override
126 | public boolean isAccountNonLocked() {
127 | return accountNonLocked;
128 | }
129 |
130 | @Override
131 | public boolean isCredentialsNonExpired() {
132 | return credentialsNonExpired;
133 | }
134 |
135 | @Override
136 | public boolean isEnabled() {
137 | return enabled;
138 | }
139 |
140 | public void setAccountNonExpired(boolean accountNonExpired) {
141 | this.accountNonExpired = accountNonExpired;
142 | }
143 |
144 | public void setAccountNonLocked(boolean accountNonLocked) {
145 | this.accountNonLocked = accountNonLocked;
146 | }
147 |
148 | public void setEnabled(boolean enabled) {
149 | this.enabled = enabled;
150 | }
151 |
152 | public void setCredentialsNonExpired(boolean credentialsNonExpired) {
153 | this.credentialsNonExpired = credentialsNonExpired;
154 | }
155 |
156 | public static final String ROLE_ADMIN = "ROLE_ADMIN";
157 | public static final String ROLE_USER = "ROLE_USER";
158 |
159 | @Override
160 | public String toString() {
161 | return "User{" +
162 | "userId=" + userId +
163 | ", username='" + username + '\'' +
164 | ", password='" + password + '\'' +
165 | ", name='" + name + '\'' +
166 | ", email='" + email + '\'' +
167 | ", registrationDate=" + registrationDate +
168 | ", roles=" + roles +
169 | '}';
170 | }
171 |
172 |
173 |
174 | }
175 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/repositories/BlogOperations.java:
--------------------------------------------------------------------------------
1 | package me.woemler.springblog.repositories;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * @author woemler
7 | */
8 | public interface BlogOperations {
9 | List findTagsByFragment(String fragment);
10 | List findAllTags();
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/repositories/BlogRepository.java:
--------------------------------------------------------------------------------
1 |
2 | package me.woemler.springblog.repositories;
3 |
4 | import me.woemler.springblog.models.BlogPost;
5 |
6 | import org.springframework.data.domain.Page;
7 | import org.springframework.data.domain.Pageable;
8 | import org.springframework.data.mongodb.repository.MongoRepository;
9 |
10 | import java.util.List;
11 |
12 | /**
13 | * @author woemler
14 | */
15 |
16 | public interface BlogRepository extends MongoRepository, BlogOperations {
17 | List findByStatusOrderByPostDateAsc(String status);
18 | Page findByStatusOrderByPostDateAsc(String status, Pageable pageable);
19 | BlogPost findBySlug(String slug);
20 | List findByTags(String tag);
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/repositories/BlogRepositoryImpl.java:
--------------------------------------------------------------------------------
1 | package me.woemler.springblog.repositories;
2 |
3 | import com.mongodb.DBCollection;
4 | import me.woemler.springblog.models.BlogPost;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.data.domain.Pageable;
7 | import org.springframework.data.mongodb.core.MongoOperations;
8 | import org.springframework.data.mongodb.core.MongoTemplate;
9 | import org.springframework.data.mongodb.core.query.Criteria;
10 | import org.springframework.data.mongodb.core.query.Query;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 | import java.util.regex.Pattern;
15 |
16 | /**
17 | * @author woemler
18 | */
19 | public class BlogRepositoryImpl implements BlogOperations {
20 |
21 | @Autowired private MongoTemplate mongoTemplate;
22 |
23 | private DBCollection getCollection(){
24 | return mongoTemplate.getCollection(mongoTemplate.getCollectionName(BlogPost.class));
25 | }
26 |
27 | @Override
28 | public List findTagsByFragment(String fragment) {
29 | Pattern pattern = Pattern.compile("^.*?" + fragment + ".*$", Pattern.CASE_INSENSITIVE);
30 | List matched = new ArrayList<>();
31 | for (String tag: this.findAllTags()){
32 | if (pattern.matcher(tag).find()){
33 | matched.add(tag);
34 | }
35 | }
36 | return matched;
37 | }
38 |
39 | @Override
40 | public List findAllTags() {
41 | return getCollection().distinct("tags");
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/repositories/UserRepository.java:
--------------------------------------------------------------------------------
1 | package me.woemler.springblog.repositories;
2 |
3 | import me.woemler.springblog.models.User;
4 | import org.springframework.data.mongodb.repository.MongoRepository;
5 | import org.springframework.security.core.userdetails.UserDetailsService;
6 |
7 | /**
8 | * @author woemler
9 | */
10 | public interface UserRepository extends MongoRepository, UserDetailsService {
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/repositories/UserRepositoryImpl.java:
--------------------------------------------------------------------------------
1 | package me.woemler.springblog.repositories;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.data.mongodb.core.MongoTemplate;
5 | import org.springframework.data.mongodb.core.query.Criteria;
6 | import org.springframework.data.mongodb.core.query.Query;
7 | import org.springframework.security.core.userdetails.UserDetails;
8 | import org.springframework.security.core.userdetails.UserDetailsService;
9 | import org.springframework.security.core.userdetails.UsernameNotFoundException;
10 |
11 | import me.woemler.springblog.models.User;
12 |
13 | /**
14 | * @author woemler
15 | */
16 | public class UserRepositoryImpl implements UserDetailsService {
17 |
18 | @Autowired private MongoTemplate mongoTemplate;
19 |
20 | @Override
21 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
22 | return mongoTemplate.findOne(new Query(Criteria.where("username").is(username)), User.class);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/me/woemler/springblog/services/GitHubService.java:
--------------------------------------------------------------------------------
1 |
2 | package me.woemler.springblog.services;
3 |
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.springframework.http.*;
8 | import org.springframework.stereotype.Service;
9 | import org.springframework.web.client.RestClientException;
10 | import org.springframework.web.client.RestTemplate;
11 |
12 | import java.io.IOException;
13 | import java.net.MalformedURLException;
14 | import java.util.ArrayList;
15 | import java.util.List;
16 | import java.util.Map;
17 |
18 | @Service
19 | public class GitHubService {
20 |
21 | private static Logger logger = LoggerFactory.getLogger(GitHubService.class);
22 |
23 | private RestTemplate restTemplate = new RestTemplate();
24 |
25 | private HttpEntity createRequest(){
26 | HttpHeaders headers = new HttpHeaders();
27 | headers.add("Accept", MediaType.APPLICATION_JSON_VALUE);
28 | return new HttpEntity(headers);
29 | }
30 |
31 | private boolean isError(HttpStatus status) {
32 | HttpStatus.Series series = status.series();
33 | return !status.equals(HttpStatus.NOT_FOUND) && (HttpStatus.Series.SERVER_ERROR.equals(series) || HttpStatus.Series.CLIENT_ERROR
34 | .equals(series));
35 | }
36 |
37 | public List