aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Vitaly Takmazov2016-12-09 16:14:27 +0300
committerGravatar Vitaly Takmazov2016-12-09 16:14:27 +0300
commit559ae343aa72db04e12b94acfa0bec30ed9a11f1 (patch)
tree5832b97c6a6a4b565de15a474bae86059763d6bb
parent22d30c3b50fe0ea4c18c45a4450fb1a8b1f1b1b8 (diff)
juick-spring-www: merge some work from develop branch, port some templates to Thymeleaf
-rw-r--r--juick-api/src/main/java/com/juick/api/controllers/Messages.java4
-rw-r--r--juick-api/src/main/java/com/juick/api/controllers/Notifications.java4
-rw-r--r--juick-api/src/main/java/com/juick/api/controllers/Others.java4
-rw-r--r--juick-api/src/main/java/com/juick/api/controllers/PM.java4
-rw-r--r--juick-api/src/main/java/com/juick/api/controllers/Post.java8
-rw-r--r--juick-api/src/main/java/com/juick/api/controllers/Subscriptions.java4
-rw-r--r--juick-api/src/main/java/com/juick/api/controllers/Users.java4
-rw-r--r--juick-api/src/test/java/com/juick/api/tests/MessagesTests.java30
-rw-r--r--juick-server/src/main/java/com/juick/server/util/HttpBadRequestException.java (renamed from juick-api/src/main/java/com/juick/api/util/HttpBadRequestException.java)2
-rw-r--r--juick-server/src/main/java/com/juick/server/util/HttpForbiddenException.java (renamed from juick-api/src/main/java/com/juick/api/util/HttpForbiddenException.java)2
-rw-r--r--juick-server/src/main/java/com/juick/server/util/HttpNotFoundException.java (renamed from juick-api/src/main/java/com/juick/api/util/HttpNotFoundException.java)2
-rw-r--r--juick-server/src/main/java/com/juick/server/util/HttpUtils.java (renamed from juick-api/src/main/java/com/juick/api/util/HttpUtils.java)2
-rw-r--r--juick-server/src/main/java/com/juick/service/EmailService.java8
-rw-r--r--juick-server/src/main/java/com/juick/service/EmailServiceImpl.java33
-rw-r--r--juick-spring-www/src/main/java/com/juick/www/controllers/HelpController.java58
-rw-r--r--juick-spring-www/src/main/java/com/juick/www/controllers/IndexController.java117
-rw-r--r--juick-spring-www/src/main/java/com/juick/www/controllers/SettingsController.java66
-rw-r--r--juick-spring-www/src/main/java/com/juick/www/controllers/TagController.java86
-rw-r--r--juick-spring-www/src/main/java/com/juick/www/helpers/QueryString.java14
-rw-r--r--juick-spring-www/src/main/java/com/juick/www/helpers/QueryStringResolver.java31
-rw-r--r--juick-spring-www/src/main/java/com/juick/www/util/EncodeUtils.java10
-rw-r--r--juick-spring-www/src/main/webapp/WEB-INF/templates/layout/mainLayout.html48
-rw-r--r--juick-spring-www/src/main/webapp/WEB-INF/templates/layout/sponsors.html8
-rw-r--r--juick-spring-www/src/main/webapp/WEB-INF/templates/views/help.html18
-rw-r--r--juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/footer.html34
-rw-r--r--juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/homecolumn.html6
-rw-r--r--juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/navigation.html37
-rw-r--r--juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/settings_tabs.html5
-rw-r--r--juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/tags.html3
-rw-r--r--juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_about.html31
-rw-r--r--juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_auth-email.html20
-rw-r--r--juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_main.html167
-rw-r--r--juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_password.html26
-rw-r--r--juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_result.html18
34 files changed, 811 insertions, 103 deletions
diff --git a/juick-api/src/main/java/com/juick/api/controllers/Messages.java b/juick-api/src/main/java/com/juick/api/controllers/Messages.java
index f30a7687..723d2f15 100644
--- a/juick-api/src/main/java/com/juick/api/controllers/Messages.java
+++ b/juick-api/src/main/java/com/juick/api/controllers/Messages.java
@@ -3,8 +3,8 @@ package com.juick.api.controllers;
import com.juick.Tag;
import com.juick.User;
import com.juick.api.ApiServer;
-import com.juick.api.util.HttpBadRequestException;
-import com.juick.api.util.HttpForbiddenException;
+import com.juick.server.util.HttpBadRequestException;
+import com.juick.server.util.HttpForbiddenException;
import com.juick.server.helpers.Status;
import com.juick.service.MessagesService;
import com.juick.service.TagService;
diff --git a/juick-api/src/main/java/com/juick/api/controllers/Notifications.java b/juick-api/src/main/java/com/juick/api/controllers/Notifications.java
index ff3679c0..cd31ad39 100644
--- a/juick-api/src/main/java/com/juick/api/controllers/Notifications.java
+++ b/juick-api/src/main/java/com/juick/api/controllers/Notifications.java
@@ -4,8 +4,8 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.juick.Message;
import com.juick.User;
-import com.juick.api.util.HttpBadRequestException;
-import com.juick.api.util.HttpForbiddenException;
+import com.juick.server.util.HttpBadRequestException;
+import com.juick.server.util.HttpForbiddenException;
import com.juick.server.helpers.Status;
import com.juick.server.helpers.TokensList;
import com.juick.service.MessagesService;
diff --git a/juick-api/src/main/java/com/juick/api/controllers/Others.java b/juick-api/src/main/java/com/juick/api/controllers/Others.java
index e2d802d8..852c03d6 100644
--- a/juick-api/src/main/java/com/juick/api/controllers/Others.java
+++ b/juick-api/src/main/java/com/juick/api/controllers/Others.java
@@ -1,8 +1,8 @@
package com.juick.api.controllers;
import com.juick.User;
-import com.juick.api.util.HttpForbiddenException;
-import com.juick.api.util.HttpNotFoundException;
+import com.juick.server.util.HttpForbiddenException;
+import com.juick.server.util.HttpNotFoundException;
import com.juick.server.helpers.PrivateChats;
import com.juick.service.PMQueriesService;
import com.juick.service.UserService;
diff --git a/juick-api/src/main/java/com/juick/api/controllers/PM.java b/juick-api/src/main/java/com/juick/api/controllers/PM.java
index c928a11e..ffbea4b5 100644
--- a/juick-api/src/main/java/com/juick/api/controllers/PM.java
+++ b/juick-api/src/main/java/com/juick/api/controllers/PM.java
@@ -2,8 +2,8 @@ package com.juick.api.controllers;
import com.juick.User;
import com.juick.api.ApiServer;
-import com.juick.api.util.HttpBadRequestException;
-import com.juick.api.util.HttpForbiddenException;
+import com.juick.server.util.HttpBadRequestException;
+import com.juick.server.util.HttpForbiddenException;
import com.juick.service.PMQueriesService;
import com.juick.service.UserService;
import com.juick.util.UserUtils;
diff --git a/juick-api/src/main/java/com/juick/api/controllers/Post.java b/juick-api/src/main/java/com/juick/api/controllers/Post.java
index 6d273f7a..53cfc3e5 100644
--- a/juick-api/src/main/java/com/juick/api/controllers/Post.java
+++ b/juick-api/src/main/java/com/juick/api/controllers/Post.java
@@ -2,10 +2,10 @@ package com.juick.api.controllers;
import com.juick.User;
import com.juick.api.ApiServer;
-import com.juick.api.util.HttpBadRequestException;
-import com.juick.api.util.HttpForbiddenException;
-import com.juick.api.util.HttpNotFoundException;
-import com.juick.api.util.HttpUtils;
+import com.juick.server.util.HttpBadRequestException;
+import com.juick.server.util.HttpForbiddenException;
+import com.juick.server.util.HttpNotFoundException;
+import com.juick.server.util.HttpUtils;
import com.juick.service.MessagesService;
import com.juick.service.SubscriptionService;
import com.juick.service.UserService;
diff --git a/juick-api/src/main/java/com/juick/api/controllers/Subscriptions.java b/juick-api/src/main/java/com/juick/api/controllers/Subscriptions.java
index 42be9903..b93ec1f3 100644
--- a/juick-api/src/main/java/com/juick/api/controllers/Subscriptions.java
+++ b/juick-api/src/main/java/com/juick/api/controllers/Subscriptions.java
@@ -2,8 +2,8 @@ package com.juick.api.controllers;
import com.juick.Message;
import com.juick.User;
-import com.juick.api.util.HttpBadRequestException;
-import com.juick.api.util.HttpForbiddenException;
+import com.juick.server.util.HttpBadRequestException;
+import com.juick.server.util.HttpForbiddenException;
import com.juick.service.MessagesService;
import com.juick.service.SubscriptionService;
import com.juick.service.UserService;
diff --git a/juick-api/src/main/java/com/juick/api/controllers/Users.java b/juick-api/src/main/java/com/juick/api/controllers/Users.java
index 396a716c..d6035cf2 100644
--- a/juick-api/src/main/java/com/juick/api/controllers/Users.java
+++ b/juick-api/src/main/java/com/juick/api/controllers/Users.java
@@ -1,8 +1,8 @@
package com.juick.api.controllers;
import com.juick.User;
-import com.juick.api.util.HttpForbiddenException;
-import com.juick.api.util.HttpNotFoundException;
+import com.juick.server.util.HttpForbiddenException;
+import com.juick.server.util.HttpNotFoundException;
import com.juick.service.UserService;
import com.juick.util.UserUtils;
import com.juick.util.WebUtils;
diff --git a/juick-api/src/test/java/com/juick/api/tests/MessagesTests.java b/juick-api/src/test/java/com/juick/api/tests/MessagesTests.java
index e0f9b840..9f74c00d 100644
--- a/juick-api/src/test/java/com/juick/api/tests/MessagesTests.java
+++ b/juick-api/src/test/java/com/juick/api/tests/MessagesTests.java
@@ -38,6 +38,7 @@ import static org.mockito.Mockito.when;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
@@ -173,12 +174,10 @@ public class MessagesTests {
@Test
public void homeTestWithMessagesAndRememberMe() throws Exception {
- String ugnichName = "ugnich";
- String uginchPassword = "MyPassw0rd!";
String msgText = "Привет, я - Угнич";
String hash = "12345678";
- User user = getUser(1, ugnichName, uginchPassword);
+ User user = getUser(1, ugnichName, ugnichPassword);
Message msg = getMessage(user, msgText);
when(userService.getUIDbyName(ugnichName))
@@ -198,7 +197,7 @@ public class MessagesTests {
mockMvc.perform(
get("/home")
- .with(httpBasic(ugnichName, uginchPassword)))
+ .with(httpBasic(ugnichName, ugnichPassword)))
.andExpect(status().isOk())
.andReturn();
@@ -211,11 +210,8 @@ public class MessagesTests {
}
@Test
- public void homeTestWithMEssagesAndSimpleCors() throws Exception {
- String ugnichName = "ugnich";
- String uginchPassword = "MyPassw0rd!";
-
- User user = getUser(1, ugnichName, uginchPassword);
+ public void homeTestWithMessagesAndSimpleCors() throws Exception {
+ User user = getUser(1, ugnichName, ugnichPassword);
Message msg = getMessage(user, null);
when(userService.getFullyUserByName(ugnichName))
@@ -229,7 +225,7 @@ public class MessagesTests {
mockMvc.perform(
get("/home")
- .with(httpBasic(ugnichName, uginchPassword))
+ .with(httpBasic(ugnichName, ugnichPassword))
.header("Origin", "http://api.example.net"))
.andExpect(status().isOk())
.andExpect(header().string("Access-Control-Allow-Origin", "*"));
@@ -237,16 +233,13 @@ public class MessagesTests {
@Test
public void homeTestWithPreflightCors() throws Exception {
- String ugnichName = "ugnich";
- String uginchPassword = "MyPassw0rd!";
-
- User user = getUser(1, ugnichName, uginchPassword);
+ User user = getUser(1, ugnichName, ugnichPassword);
when(userService.getFullyUserByName(ugnichName))
.thenReturn(user);
mockMvc.perform(
options("/home")
- .with(httpBasic(ugnichName, uginchPassword))
+ .with(httpBasic(ugnichName, ugnichPassword))
.header("Origin", "http://api.example.net")
.header("Access-Control-Request-Method", "POST")
.header("Access-Control-Request-Headers", "X-PINGOTHER, Content-Type"))
@@ -291,4 +284,11 @@ public class MessagesTests {
.andExpect(jsonPath("$", hasSize(1)))
.andExpect(jsonPath("$[0].messages", is(5)));
}
+ @Test
+ public void postWithReferer() throws Exception {
+ mockMvc.perform(post("/post")
+ .param("body", "yo")
+ .with(httpBasic(ugnichName, ugnichPassword)))
+ .andExpect(status().isOk());
+ }
}
diff --git a/juick-api/src/main/java/com/juick/api/util/HttpBadRequestException.java b/juick-server/src/main/java/com/juick/server/util/HttpBadRequestException.java
index 3a42e999..cd862830 100644
--- a/juick-api/src/main/java/com/juick/api/util/HttpBadRequestException.java
+++ b/juick-server/src/main/java/com/juick/server/util/HttpBadRequestException.java
@@ -1,4 +1,4 @@
-package com.juick.api.util;
+package com.juick.server.util;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
diff --git a/juick-api/src/main/java/com/juick/api/util/HttpForbiddenException.java b/juick-server/src/main/java/com/juick/server/util/HttpForbiddenException.java
index d8f4ddf3..7e85816e 100644
--- a/juick-api/src/main/java/com/juick/api/util/HttpForbiddenException.java
+++ b/juick-server/src/main/java/com/juick/server/util/HttpForbiddenException.java
@@ -1,4 +1,4 @@
-package com.juick.api.util;
+package com.juick.server.util;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
diff --git a/juick-api/src/main/java/com/juick/api/util/HttpNotFoundException.java b/juick-server/src/main/java/com/juick/server/util/HttpNotFoundException.java
index 14f34cf0..378eadfc 100644
--- a/juick-api/src/main/java/com/juick/api/util/HttpNotFoundException.java
+++ b/juick-server/src/main/java/com/juick/server/util/HttpNotFoundException.java
@@ -1,4 +1,4 @@
-package com.juick.api.util;
+package com.juick.server.util;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
diff --git a/juick-api/src/main/java/com/juick/api/util/HttpUtils.java b/juick-server/src/main/java/com/juick/server/util/HttpUtils.java
index ca75eae7..fa0b2ec2 100644
--- a/juick-api/src/main/java/com/juick/api/util/HttpUtils.java
+++ b/juick-server/src/main/java/com/juick/server/util/HttpUtils.java
@@ -15,7 +15,7 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package com.juick.api.util;
+package com.juick.server.util;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
diff --git a/juick-server/src/main/java/com/juick/service/EmailService.java b/juick-server/src/main/java/com/juick/service/EmailService.java
new file mode 100644
index 00000000..f62f4b32
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/service/EmailService.java
@@ -0,0 +1,8 @@
+package com.juick.service;
+
+/**
+ * Created by vitalyster on 09.12.2016.
+ */
+public interface EmailService {
+ boolean verifyAddressByCode(Integer userId, String code);
+}
diff --git a/juick-server/src/main/java/com/juick/service/EmailServiceImpl.java b/juick-server/src/main/java/com/juick/service/EmailServiceImpl.java
new file mode 100644
index 00000000..bdd78609
--- /dev/null
+++ b/juick-server/src/main/java/com/juick/service/EmailServiceImpl.java
@@ -0,0 +1,33 @@
+package com.juick.service;
+
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.inject.Inject;
+
+/**
+ * Created by vitalyster on 09.12.2016.
+ */
+@Repository
+@Transactional
+public class EmailServiceImpl extends BaseJdbcService implements EmailService {
+ @Inject
+ public EmailServiceImpl(JdbcTemplate jdbcTemplate) {
+ super(jdbcTemplate, null);
+ }
+
+ @Override
+ public boolean verifyAddressByCode(Integer userId, String code) {
+ try {
+ String address = getJdbcTemplate().queryForObject("SELECT account FROM auth WHERE user_id=? AND protocol='email' AND authcode=?",
+ String.class, userId, code);
+ getJdbcTemplate().update("INSERT INTO emails(user_id,email) VALUES (?,?)", userId, address);
+ getJdbcTemplate().update("DELETE FROM auth WHERE user_id=? AND authcode=?", userId, code);
+ } catch (EmptyResultDataAccessException e) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/juick-spring-www/src/main/java/com/juick/www/controllers/HelpController.java b/juick-spring-www/src/main/java/com/juick/www/controllers/HelpController.java
index dad3ff9f..49e24c8c 100644
--- a/juick-spring-www/src/main/java/com/juick/www/controllers/HelpController.java
+++ b/juick-spring-www/src/main/java/com/juick/www/controllers/HelpController.java
@@ -1,12 +1,68 @@
package com.juick.www.controllers;
+import com.juick.User;
+import com.juick.server.util.HttpNotFoundException;
+import com.juick.service.UserService;
+import com.juick.util.UserUtils;
+import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
+import javax.inject.Inject;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.Principal;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
/**
* Created by aalexeev on 11/21/16.
*/
@Controller
-@RequestMapping("/help")
public class HelpController {
+ @Inject
+ UserService userService;
+
+ @RequestMapping({"/help", "/help/{lang}", "/help/{lang}/{page}"})
+ protected String doGetHelp(
+ Principal principal,
+ @PathVariable("lang") Optional<String> lang,
+ @PathVariable("page") Optional<String> page,
+ ModelMap model) throws IOException, URISyntaxException {
+ String name = UserUtils.getUsername(principal, null);
+ User visitor = userService.getUserByName(name);
+
+ lang.ifPresent(l -> {
+ if (l.length() != 2 || !l.matches("^[a-z]+$")) {
+ throw new HttpNotFoundException();
+ }
+ });
+ if (!lang.isPresent()) {
+ lang = Optional.of("ru");
+ }
+
+ page.ifPresent(p -> {
+ if (!p.matches("^[a-zA-Z0-9\\-]*$") || p.equals("navigation") || p.equals("index")) {
+ throw new HttpNotFoundException();
+ }
+ });
+ if (!page.isPresent()) {
+ page = Optional.of("index");
+ }
+
+ String filePath = "help/" + lang.get() + "/" + page.get();
+ String navigationPath = "help/" + lang.get() + "/navigation";
+
+ model.addAttribute("title", "Помощь");
+ model.addAttribute("visitor", visitor);
+ model.addAttribute("help_nav", Files.readAllLines(Paths.get(new ClassPathResource(navigationPath).getURI()))
+ .stream().collect(Collectors.joining()));
+ model.addAttribute("help_data", Files.readAllLines(Paths.get(new ClassPathResource(filePath).getURI()))
+ .stream().collect(Collectors.joining()));
+ return "views/help";
+ }
}
diff --git a/juick-spring-www/src/main/java/com/juick/www/controllers/IndexController.java b/juick-spring-www/src/main/java/com/juick/www/controllers/IndexController.java
index 2973bf30..91efd8aa 100644
--- a/juick-spring-www/src/main/java/com/juick/www/controllers/IndexController.java
+++ b/juick-spring-www/src/main/java/com/juick/www/controllers/IndexController.java
@@ -1,9 +1,26 @@
package com.juick.www.controllers;
+import com.juick.Message;
+import com.juick.User;
+import com.juick.service.MessagesService;
+import com.juick.service.TagService;
+import com.juick.service.UserService;
+import com.juick.util.UserUtils;
+import com.juick.www.util.EncodeUtils;
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.*;
+
+import javax.inject.Inject;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
/**
* Created by aalexeev on 11/21/16.
@@ -11,14 +28,94 @@ import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class IndexController {
- @RequestMapping("/")
- public String index(
- java.security.Principal userPrincipal,
- @RequestParam(required = false, defaultValue = "0") int before,
- Model model) {
+ @Inject
+ UserService userService;
+ @Inject
+ MessagesService messagesService;
+ @Inject
+ TagService tagService;
+
+ @RequestMapping(value = "/", method = RequestMethod.GET)
+ protected String doGet(
+ Principal principal,
+ @CookieValue("ref") Optional<String> ref,
+ @RequestHeader("Referer") Optional<String> referer,
+ @RequestParam("show") Optional<String> paramShow,
+ @RequestParam("tag") Optional<String> paramTagStr,
+ @RequestParam(value = "before", required = false) Integer paramBefore,
+ @RequestParam(value = "search", required = false) String paramSearch,
+ ModelMap model) throws IOException {
+
+ if (paramTagStr.isPresent()) {
+ return "redirect:/tag/" + URLEncoder.encode(paramTagStr.get(), "UTF-8");
+ }
+
+ if (StringUtils.isNotEmpty(paramSearch) && paramSearch.length() > 64) {
+ paramSearch = "";
+ }
+
+ String name = UserUtils.getUsername(principal, null);
+ User visitor = userService.getUserByName(name);
- model.addAttribute("currentUser", userPrincipal);
+ String title;
+ if (visitor.getUid() > 0) {
+ title = "Популярные";
+ } else {
+ title = "Микроблоги Juick: популярные записи";
+ }
+ List<Integer> mids = new ArrayList<>();
- return "index";
+ if (StringUtils.isNotEmpty(paramSearch)) {
+ title = "Поиск: " + StringEscapeUtils.escapeHtml4(paramSearch);
+ mids = messagesService.getSearch(EncodeUtils.encodeSphinx(paramSearch),
+ paramBefore);
+ } else if (!paramShow.isPresent()) {
+ mids = messagesService.getPopular(visitor.getUid(), paramBefore);
+ } else if (paramShow.get().equals("top")) {
+ return "redirect:/";
+ } else if (paramShow.get().equals("my") && visitor.getUid() > 0) {
+ title = "Моя лента";
+ mids = messagesService.getMyFeed(visitor.getUid(), paramBefore);
+ } else if (paramShow.get().equals("private") && visitor.getUid() > 0) {
+ title = "Приватные";
+ mids = messagesService.getPrivate(visitor.getUid(), paramBefore);
+ } else if (paramShow.get().equals("discuss") && visitor.getUid() > 0) {
+ title = "Обсуждения";
+ mids = messagesService.getDiscussions(visitor.getUid(), paramBefore);
+ } else if (paramShow.get().equals("recommended") && visitor.getUid() > 0) {
+ title = "Рекомендации";
+ mids = messagesService.getRecommended(visitor.getUid(), paramBefore);
+ } else if (paramShow.get().equals("photos")) {
+ title = "Фотографии";
+ mids = messagesService.getPhotos(visitor.getUid(), paramBefore);
+ } else if (paramShow.get().equals("all")) {
+ title = "Все сообщения";
+ mids = messagesService.getAll(visitor.getUid(), paramBefore);
+ }
+ model.addAttribute("title", title);
+ model.addAttribute("visitor", visitor);
+ model.addAttribute("tags", tagService.getPopularTags());
+ model.addAttribute("showAdv", true);
+ List<Message> msgs = messagesService.getMessages(mids);
+ List<Integer> blUIDs = userService.checkBL(visitor.getUid(),
+ msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList()));
+ model.addAttribute("msgs",
+ msgs.stream().map(msg -> {
+ msg.ReadOnly |= blUIDs.contains(msg.getUser().getUid());
+ return msg;
+ }).collect(Collectors.toList())
+ );
+ if (mids.size() >= 20) {
+ String nextpage = "?before=" + mids.get(mids.size() - 1);
+ if (paramShow.isPresent()) {
+ nextpage += "&show=" + paramShow.get();
+ }
+ if (StringUtils.isNotEmpty(paramSearch)) {
+ nextpage += "&search=" + paramSearch;
+ }
+ model.addAttribute("nextpage", nextpage);
+ }
+ //model.addAttribute("isModerator", userService.getModerators().contains(visitor.getUid()));
+ return "blog/index";
}
}
diff --git a/juick-spring-www/src/main/java/com/juick/www/controllers/SettingsController.java b/juick-spring-www/src/main/java/com/juick/www/controllers/SettingsController.java
index eef41c38..a038389f 100644
--- a/juick-spring-www/src/main/java/com/juick/www/controllers/SettingsController.java
+++ b/juick-spring-www/src/main/java/com/juick/www/controllers/SettingsController.java
@@ -1,16 +1,76 @@
package com.juick.www.controllers;
+import com.juick.User;
+import com.juick.service.*;
+import com.juick.util.UserUtils;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import javax.inject.Inject;
+import javax.servlet.ServletException;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
/**
* Created by aalexeev on 11/21/16.
*/
@Controller
public class SettingsController {
+ @Inject
+ UserService userService;
+ @Inject
+ TagService tagService;
+ @Inject
+ CrosspostService crosspostService;
+ @Inject
+ SubscriptionService subscriptionService;
+ @Inject
+ EmailService emailService;
- @RequestMapping("/settings")
- public String showSettings() {
- return "index";
+ @RequestMapping("settings")
+ public String showSettings(
+ Principal principal,
+ @RequestParam(required = false) String page,
+ @RequestParam(required = false) String code,
+ ModelMap context
+ ) throws ServletException, IOException {
+ String name = UserUtils.getUsername(principal, null);
+ User visitor = userService.getUserByName(name);
+ if (visitor.getUid() == 0) {
+ return "redirect:/login";
+ }
+ List<String> pages = Arrays.asList("main", "password", "about", "auth-email", "privacy");
+ if (StringUtils.isEmpty(page) || !pages.contains(page)) {
+ page = "main";
+ }
+ context.put("title", "Настройки");
+ context.put("visitor", visitor);
+ context.put("tags", tagService.getPopularTags());
+ context.put("auths", userService.getAuthCodes(visitor));
+ context.put("eopts", userService.getEmailOpts(visitor));
+ context.put("ehash", userService.getEmailHash(visitor));
+ context.put("emails", userService.getEmails(visitor));
+ context.put("jids", userService.getAllJIDs(visitor));
+ List<String> hours = IntStream.rangeClosed(0, 23).boxed()
+ .map(i -> StringUtils.leftPad(String.format("%d", i), 2, "0")).collect(Collectors.toList());
+ context.put("hours", hours);
+ context.put("fbstatus", crosspostService.getFbCrossPostStatus(visitor.getUid()));
+ context.put("twitter_name", crosspostService.getTwitterName(visitor.getUid()));
+ context.put("telegram_name", crosspostService.getTelegramName(visitor.getUid()));
+ context.put("notify_options", subscriptionService.getNotifyOptions(visitor));
+ context.put("userinfo", userService.getUserInfo(visitor));
+ if (page.equals("auth-email")) {
+ String response = emailService.verifyAddressByCode(visitor.getUid(), code) ?
+ "OK!" : "Sorry, code unknown.";
+ context.put("result", response);
+ }
+ return String.format("views/settings_%s", page);
}
}
diff --git a/juick-spring-www/src/main/java/com/juick/www/controllers/TagController.java b/juick-spring-www/src/main/java/com/juick/www/controllers/TagController.java
index 5c3b6287..0961f683 100644
--- a/juick-spring-www/src/main/java/com/juick/www/controllers/TagController.java
+++ b/juick-spring-www/src/main/java/com/juick/www/controllers/TagController.java
@@ -1,14 +1,28 @@
package com.juick.www.controllers;
+import com.juick.Message;
+import com.juick.User;
+import com.juick.server.util.HttpNotFoundException;
+import com.juick.service.AdsService;
import com.juick.service.MessagesService;
import com.juick.service.TagService;
+import com.juick.service.UserService;
+import com.juick.util.UserUtils;
+import com.juick.www.helpers.QueryString;
+import org.apache.commons.lang3.StringEscapeUtils;
import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
+import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.inject.Inject;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.security.Principal;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
/**
* Created by aalexeev on 11/21/16.
@@ -19,15 +33,75 @@ public class TagController {
private TagService tagService;
@Inject
private MessagesService messagesService;
+ @Inject
+ private AdsService adsService;
+ @Inject
+ private UserService userService;
@RequestMapping("/tag/{tagName}")
- public String showTags(
- @PathVariable String tagName,
- @RequestParam(required = false, defaultValue = "0") int before,
- Model model) {
+ protected String doGet(
+ Principal principal,
+ @PathVariable("tagName") String paramTagStr,
+ @RequestParam(value = "before", required = false, defaultValue = "0") Integer paramBefore,
+ @QueryString Optional<String> queryString,
+ ModelMap model) throws UnsupportedEncodingException {
+ String name = UserUtils.getUsername(principal, null);
+ User visitor = userService.getUserByName(name);
+
+ com.juick.Tag paramTag = tagService.getTag(paramTagStr, false);
+ if (paramTag == null) {
+ throw new HttpNotFoundException();
+ } else if (paramTag.SynonymID > 0 && paramTag.TID != paramTag.SynonymID) {
+ com.juick.Tag synTag = tagService.getTag(paramTag.SynonymID);
+ String url = "/tag/" + URLEncoder.encode(synTag.getName(), "UTF-8");
+ if (queryString.isPresent()) {
+ url += "?" + queryString.get();
+ }
+ return "redirect:" + url;
+ } else if (!paramTag.getName().equals(paramTagStr)) {
+ String url = "/tag/" + URLEncoder.encode(paramTag.getName(), "UTF-8");
+ if (queryString.isPresent()) {
+ url += "?" + queryString.get();
+ }
+ return "redirect:" + url;
+ }
+
+ int visitor_uid = visitor != null ? visitor.getUid() : 0;
- return "index";
+ String title = "*" + StringEscapeUtils.escapeHtml4(paramTag.getName());
+ List<Integer> mids = messagesService.getTag(paramTag.TID, visitor_uid, paramBefore, (visitor == null) ? 40 : 20);
+ model.addAttribute("title", title);
+ if (tagService.getTagNoIndex(paramTag.TID)) {
+ model.addAttribute("headers", "<meta name=\"robots\" content=\"noindex,nofollow\"/>");
+ } else if (paramBefore > 0 || mids.size() < 5) {
+ model.addAttribute("headers", "<meta name=\"robots\" content=\"noindex\"/>");
+ }
+ if (mids.size() > 0) {
+ int vuid = visitor != null ? visitor.getUid() : 0;
+ int ad_mid = adsService.getAdMid(vuid);
+ if (ad_mid > 0 && mids.indexOf(ad_mid) == -1) {
+ mids.add(0, ad_mid);
+ adsService.logAdMid(vuid, ad_mid);
+ } else {
+ ad_mid = 0;
+ }
+ }
+ model.addAttribute("visitor", visitor);
+ List<Message> msgs = messagesService.getMessages(mids);
+ List<Integer> blUIDs = userService.checkBL(visitor_uid,
+ msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList()));
+ model.addAttribute("msgs",
+ msgs.stream().map(msg -> {
+ msg.ReadOnly |= blUIDs.contains(msg.getUser().getUid());
+ return msg;
+ }).collect(Collectors.toList())
+ );
+ if (mids.size() >= 20) {
+ String nextpage = "/tag/" + URLEncoder.encode(paramTag.getName(), "UTF-8") + "?before=" + mids.get(mids.size() - 1);
+ model.addAttribute("nextpage", nextpage);
+ }
+ return "blog/index";
}
@RequestMapping("/tag")
diff --git a/juick-spring-www/src/main/java/com/juick/www/helpers/QueryString.java b/juick-spring-www/src/main/java/com/juick/www/helpers/QueryString.java
new file mode 100644
index 00000000..0eb6c76f
--- /dev/null
+++ b/juick-spring-www/src/main/java/com/juick/www/helpers/QueryString.java
@@ -0,0 +1,14 @@
+package com.juick.www.helpers;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Created by vt on 22/03/16.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface QueryString {
+} \ No newline at end of file
diff --git a/juick-spring-www/src/main/java/com/juick/www/helpers/QueryStringResolver.java b/juick-spring-www/src/main/java/com/juick/www/helpers/QueryStringResolver.java
new file mode 100644
index 00000000..812c4497
--- /dev/null
+++ b/juick-spring-www/src/main/java/com/juick/www/helpers/QueryStringResolver.java
@@ -0,0 +1,31 @@
+package com.juick.www.helpers;
+
+import org.springframework.core.MethodParameter;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.annotation.Annotation;
+import java.util.Optional;
+
+public class QueryStringResolver implements HandlerMethodArgumentResolver {
+ @Override
+ public boolean supportsParameter(MethodParameter parameter) {
+ Annotation[] parameterAnnotations = parameter.getParameterAnnotations();
+ for (Annotation parameterAnnotation : parameterAnnotations) {
+ if (QueryString.class.isInstance(parameterAnnotation)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
+ HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
+ return Optional.ofNullable(request.getQueryString());
+ }
+} \ No newline at end of file
diff --git a/juick-spring-www/src/main/java/com/juick/www/util/EncodeUtils.java b/juick-spring-www/src/main/java/com/juick/www/util/EncodeUtils.java
new file mode 100644
index 00000000..a444ec4d
--- /dev/null
+++ b/juick-spring-www/src/main/java/com/juick/www/util/EncodeUtils.java
@@ -0,0 +1,10 @@
+package com.juick.www.util;
+
+/**
+ * Created by vitalyster on 09.12.2016.
+ */
+public class EncodeUtils {
+ public static String encodeSphinx(String str) {
+ return str.replaceAll("@", "\\\\@");
+ }
+}
diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/layout/mainLayout.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/layout/mainLayout.html
index bbee7f6a..61062a3b 100644
--- a/juick-spring-www/src/main/webapp/WEB-INF/templates/layout/mainLayout.html
+++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/layout/mainLayout.html
@@ -11,53 +11,21 @@
<script type="text/javascript" src="/scripts.js" th:href="@{/scripts.js}"></script>
<title layout:title-pattern="$LAYOUT_TITLE - $CONTENT_TITLE">Juick.com</title>
-
+ <meta layout:fragment="headers" />
<link rel="icon" href="//i.juick.com/favicon.png"/>
<!--[if lt IE 9 & (!IEMobile 7)]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
<![endif]-->
</head>
<body>
-
-<section layout:fragment="content">
+<div th:replace="views/partial/navigation">Navigation block</div>
+<section id="content">
+ <p layout:fragment="content">Main content</p>
</section>
+<aside id="column">
+ <p layout:fragment="column">Side column</p>
+</aside>
-<footer>
- <div layout:fragment="custom-footer"></div>
- <div id="footer">
- <div id="footer-right">
- <a href="/settings" th:href="@{/settings}" rel="nofollow" th:text="#{link.settings}">Настройки</a> &#183;
- <a href="/help/contacts" th:href="@{/help/contacts}" rel="nofollow" th:text="#{link.contacts}">Контакты</a> &#183;
- <a href="/help/" th:href="@{/help}" rel="nofollow" th:text="#{link.help}">Справка</a> &#183;
- <a href="/help/adv" th:href="@{/help/adv}" rel="nofollow" th:text="#{link.adv}">Реклама</a>
- </div>
- <div id="footer-social">
- <a href="https://twitter.com/Juick" rel="nofollow" class="ico32-twi">Twitter</a>
- <a href="https://vk.com/juick" rel="nofollow" class="ico32-vk">VK</a>
- <a href="https://www.facebook.com/JuickCom" rel="nofollow" class="ico32-fb">Facebook</a>
- </div>
- <div id="footer-left">
- <a href="juick.com">juick.com</a> &copy; 2008-2016
- <div th:replace="${@settingsHolder.isShowSponsors()} ? ~{layout/sponsors :: sponsors} : _"></div>
- </div>
- </div>
- <script>
- (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
- (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
- m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
- })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
- ga('create','UA-385578-4','juick.com');
- ga('require','displayfeatures');
- ga('send','pageview');
- </script>
- <script th:if="${@settingsHolder.isShowSape()}">
- var _acic={dataProvider:10};
- (function(){
- var e=document.createElement('script');e.type='text/javascript';e.async=true;e.src='//www2.aci'+'nt.net/aci.js';
- var t=document.getElementsByTagName('script')[0];t.parentNode.insertBefore(e,t);
- })();
- </script>
- </div>
-</footer>
+<footer th:replace="views/partial/footer">Footer</footer>
</body>
</html> \ No newline at end of file
diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/layout/sponsors.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/layout/sponsors.html
deleted file mode 100644
index 6c68a867..00000000
--- a/juick-spring-www/src/main/webapp/WEB-INF/templates/layout/sponsors.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<!DOCTYPE html>
-<html xmlns:th="http://www.thymeleaf.org">
-<body>
-<div th:fragment="sponsors">
- <span th:text="#{label.sponsors}">Спонсоры:</span>
-</div>
-</body>
-</html> \ No newline at end of file
diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/views/help.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/help.html
new file mode 100644
index 00000000..0500839b
--- /dev/null
+++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/help.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org"
+ xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+ layout:decorate="~{layout/mainLayout}">
+<head>
+ <title th:text="${title}">Help title</title>
+</head>
+
+<body>
+<section id="content">
+ <article layout:fragment="content" th:utext="${help_data}">Help text</article>
+</section>
+<aside id="column">
+ <p layout:fragment="column" th:utext="${help_nav}">Help navigation</p>
+</aside>
+
+</body>
+</html> \ No newline at end of file
diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/footer.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/footer.html
new file mode 100644
index 00000000..6978a2c0
--- /dev/null
+++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/footer.html
@@ -0,0 +1,34 @@
+<div id="footer">
+ <div id="footer-right">
+ <a href="/settings" rel="nofollow">Настройки</a> &#183;
+ <a href="/help/ru/contacts" rel="nofollow">Контакты</a> &#183;
+ <a href="/help/" rel="nofollow">Справка</a> &#183;
+ <a href="/help/ru/adv" rel="nofollow">Реклама</a>
+ </div>
+ <div id="footer-social">
+ <a href="https://twitter.com/Juick" rel="nofollow" class="ico32-twi">Twitter</a>
+ <a href="https://vk.com/juick" rel="nofollow" class="ico32-vk">ВКонтакте</a>
+ <a href="https://www.facebook.com/JuickCom" rel="nofollow" class="ico32-fb">Facebook</a>
+ </div>
+ <div id="footer-left">juick.com &copy; 2008-2016
+ {% if links %}
+ <br/>Спонсоры: {{ links }}
+ {% endif %}
+ </div>
+</div>
+<script>
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+ ga('create','UA-385578-4','juick.com');
+ ga('require','displayfeatures');
+ ga('send','pageview');
+ {% if (sapeon) %}
+ var _acic={dataProvider:10};
+ (function(){
+ var e=document.createElement('script');e.type='text/javascript';e.async=true;e.src='//www2.aci'+'nt.net/aci.js';
+ var t=document.getElementsByTagName('script')[0];t.parentNode.insertBefore(e,t);
+ })();
+ {% endif %}
+</script> \ No newline at end of file
diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/homecolumn.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/homecolumn.html
new file mode 100644
index 00000000..5293918d
--- /dev/null
+++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/homecolumn.html
@@ -0,0 +1,6 @@
+<p class="tags">
+ {% include "views/partial/tags.html" %}
+ {% if showAdv %}
+ <a href="http://ru.wix.com/">конструктор сайтов</a>
+ {% endif %}
+</p> \ No newline at end of file
diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/navigation.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/navigation.html
new file mode 100644
index 00000000..2863d0c3
--- /dev/null
+++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/navigation.html
@@ -0,0 +1,37 @@
+<header>
+ <div id="logo"><a href="/">Juick</a></div>
+ <nav id="global">
+ <ul>
+ <li><a href="/">Популярные</a></li>
+ <li><a href="/?show=all" rel="nofollow">Все сообщения</a></li>
+ <li><a href="/?show=photos" rel="nofollow">Фотографии</a></li>
+ </ul>
+ </nav>
+ <div id="search">
+ <form action="/">
+ <input type="text" name="search" class="text" placeholder="Поиск" value="{{ search }}"/>
+ </form>
+ </div>
+ <section id="headdiv">
+ {% if visitor.getUID() > 0 %}
+ <nav id="user">
+ <ul>
+ <li><a href="/?show=my">Моя лента</a></li>
+ <li><a href="/pm/inbox">Приватные</a></li>
+ <li><a href="/?show=discuss">Обсуждения</a></li>
+ <li><a href="/?show=recommended">Рекомендации</a></li>
+ </ul>
+ </nav>
+ <nav id="actions">
+ <ul>
+ <li><a href="/#post">Написать</a></li>
+ <li><a href="/{{ visitor.getName() }}">@{{ visitor.getName() }}</a></li>
+ <li><a href="/logout">Выйти</a></li>
+ </ul>
+ </nav>
+ {% else %}
+ <p>Чтобы добавлять сообщения и комментарии, <a href="#" onclick="return Juick.openDialogLogin()">представьтесь</a>.
+ </p>
+ {% endif %}
+ </section>
+</header>
diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/settings_tabs.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/settings_tabs.html
new file mode 100644
index 00000000..d7901d5e
--- /dev/null
+++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/settings_tabs.html
@@ -0,0 +1,5 @@
+<div id="pagetabs"><ul>
+ <li><a href="/settings">Main</a></li>
+ <li><a href="/settings?page=password">Password</a></li>
+ <li><a href="/settings?page=about">About</a></li>
+</ul></div> \ No newline at end of file
diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/tags.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/tags.html
new file mode 100644
index 00000000..4d05b7fb
--- /dev/null
+++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/partial/tags.html
@@ -0,0 +1,3 @@
+{% for tag in tags %}
+ <a href="/tag/{{ tag | urlencode }}" title="{{ tag }}">{{ tag }}</a>
+{% endfor %} \ No newline at end of file
diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_about.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_about.html
new file mode 100644
index 00000000..ff82b542
--- /dev/null
+++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_about.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org"
+ xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+ layout:decorate="~{layout/mainLayout}">
+<head>
+ <title th:text="${title}">Settings title</title>
+</head>
+
+<body>
+<section id="content">
+ <article layout:fragment="content">
+ <form action="/settings" method="POST" enctype="multipart/form-data">
+ <p>Full name: <input type="text" name="fullname" value="{{ userinfo.getFullName() }}"/></p>
+ <p>Country: <input type="text" name="country" value="{{ userinfo.getCountry() }}"/></p>
+ <p>URL: <input type="text" name="url" value="{{ userinfo.getUrl() }}" size="32"/><br/>
+ <small>Please, start with &quot;http://&quot;</small></p>
+ <p>About:<br/>
+ <input type="text" name="descr" value="{{ userinfo.getDescription() }}" style="width: 100%"/><br/>
+ <small>Max. 255 symbols</small></p>
+ <p>Avatar: <input type="file" name="avatar"/><br/>
+ <small>Recommendations: PNG, 96x96, &lt;50Kb. Also, JPG and GIF supported.</small></p>
+ <p><input type="hidden" name="page" value="about"/><input type="submit" value=" OK "/></p>
+ </form>
+ </article>
+</section>
+<aside id="column">
+ <p th:replace="views/partial/settings/tabs">Settings navigation</p>
+</aside>
+
+</body>
+</html> \ No newline at end of file
diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_auth-email.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_auth-email.html
new file mode 100644
index 00000000..d4788fd6
--- /dev/null
+++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_auth-email.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org"
+ xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+ layout:decorate="~{layout/mainLayout}">
+<head>
+ <title th:text="${title}">Settings title</title>
+</head>
+
+<body>
+<section id="content">
+ <article layout:fragment="content">
+ <p th:text="${result}">Authentication result</p><p><a href="/settings">Settings</a>.</p>
+ </article>
+</section>
+<aside id="column">
+ <p th:replace="views/partial/settings/tabs">Settings navigation</p>
+</aside>
+
+</body>
+</html> \ No newline at end of file
diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_main.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_main.html
new file mode 100644
index 00000000..42eff2e3
--- /dev/null
+++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_main.html
@@ -0,0 +1,167 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org"
+ xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+ layout:decorate="~{layout/mainLayout}">
+<head>
+ <title th:text="${title}">Settings title</title>
+</head>
+
+<body>
+<section id="content">
+ <article layout:fragment="content">
+ <h1>Настройки</h1>
+ <form action="/settings" method="POST" enctype="multipart/form-data">
+ <fieldset>
+ <legend>Notification options</legend>
+ <p><input type="checkbox" name="jnotify" value="1" {% if notify_options.isRepliesEnabled() %}
+ checked="checked" {% endif %}/> Reply notifications (&quot;Message posted&quot;)</p>
+ <p><input type="checkbox" name="subscr_notify" value="1" {% if notify_options.isSubscriptionsEnabled() %}
+ checked="checked" {% endif %}/> Subscriptions notifications (&quot;@user subscribed...&quot;)</p>
+ <p><input type="checkbox" name="recomm" value="1" {% if notify_options.isRecommendationsEnabled() %}
+ checked="checked" {% endif %}/> Posts recommendations (&quot;Recommended by @user&quot;)</p>
+ <p><input type="hidden" name="page" value="main"/><input type="submit" value=" OK "/></p>
+ </fieldset>
+ </form>
+ <fieldset>
+ <legend style="background: url(//telegram.org/favicon.ico?3) no-repeat; padding-left: 58px; line-height: 48px;">
+ Telegram</legend>
+ {% if telegram_name is not empty %}
+ <form action="/settings" method="post">
+ <div>Telegram: <b>{{ telegram_name }}</b> &mdash;
+ <input type="hidden" name="page" value="telegram-del"/>
+ <input type="submit" value=" Disable "/>
+ </div>
+ </form>
+ {% else %}
+ <p>To connect Telegram account: send any text message to <a href="https://telegram.me/Juick_bot">@Juick_bot</a>
+ </p>
+ {% endif %}
+ </fieldset>
+ <form action="/settings" method="POST" enctype="multipart/form-data">
+ <fieldset>
+ <legend style="background: url(//static.juick.com/settings/xmpp.png) no-repeat; padding-left: 58px; line-height: 48px;">
+ XMPP accounts
+ </legend>
+ <p>Your accounts:</p>
+ <p>
+ {% for jid in jids %}
+ <label><input type="radio" name="delete" value="xmpp;{{ jid }}">{{ jid }}</label><br/>
+ {% endfor %}
+ {% for auth in auths %}
+ <label><input type="radio" name="delete"
+ value="xmpp-unauth;{{ auth.getAccount() }}">{{ auth.getAccount() }}</label>
+ &mdash; <a href="#"
+ onclick="alert(\'To confirm, please send &quot;AUTH {{ auth.getAuthCode() }}&quot; (without quotes) from this account to &quot;juick@juick.com&quot;.\'); return false;">Confirm</a><br/>
+ {% endfor %}
+ </p>
+ {% if jids is not empty %}
+ <p><input type="hidden" name="page" value="jid-del"/><input type="submit" value=" Delete "/></p>
+ {% endif %}
+ <p>To add new jabber account: send any text message to <a href="xmpp:juick@juick.com?message;body=login">juick@juick.com</a>
+ </p>
+ </fieldset>
+ </form>
+ <fieldset>
+ <legend style="background: url(//static.juick.com/settings/email.png) no-repeat; padding-left: 58px; line-height: 48px;">
+ E-mail
+ </legend>
+ <form action="/settings" method="POST" enctype="multipart/form-data">
+ <p>Add account:<br/>
+ <input type="text" name="account"/>
+ <input type="hidden" name="page" value="email-add"/>
+ <input type="submit" value=" Add "/>
+ </p>
+ </form>
+ <form action="/settings" method="POST" enctype="multipart/form-data">
+ <p>Your accounts:</p>
+ <p>
+ {% for email in emails %}
+ <label><input type="radio" name="account" value="{{ email }}">{{ email }}</label><br/>
+ {% endfor %}
+ {% if emails is empty %}
+ - </p>
+ {% else %}
+ </p>
+ <p><input type="hidden" name="page" value="email-del"/><input type="submit" value=" Delete "/></p>
+ {% endif %}
+ </form>
+ {% if emails is not empty %}
+ <form action="/settings" method="POST" enctype="multipart/form-data">
+ <p>You can receive all your subscriptions by email:<br/>
+ Sent to <select name="account">
+ <option value="">Disabled</option>
+ {% for email in emails %}
+ <option value="{{ email }}" {% if eopts.getEmail()== email %} selected="selected" {% endif %}>
+ {{ email }}
+ </option>
+ {% endfor %}
+ </select> every day at <select name="time">
+ {% for hour in hours %}
+ <option value="{{ hour }}" {% if eopts.getSubscriptionHour() == hour %} selected="selected" {%
+ endif %}>
+ {{ hour }}:00 GMT
+ </option>
+ {% endfor %}
+ </select>
+ <input type="hidden" name="page" value="email-subscr"/>
+ <input type="submit" value="OK"/></p>
+ </form>
+ {% endif %}
+ <p>&nbsp;</p>
+ <p>You can post to Juick via e-mail. Send your <span style="text-decoration: underline">plain text</span>
+ messages to special secret e-mail. You can attach one photo or video file.</p>
+ <p>Secret email: {% if ehash is not empty %} <strong>{{ ehash }}</strong> {% else %}-{% endif %}</p>
+ <form action="/settings" method="post">
+ <p><input type="hidden" name="page" value="email"/><input type="submit" value=" Generate new "/></p>
+ </form>
+ </fieldset>
+ <fieldset>
+ <legend style="background: url(//static.juick.com/settings/facebook.png) no-repeat; padding-left: 58px; line-height: 48px;">
+ Facebook
+ </legend>
+ {% if fbstatus.isConnected() %}
+ {% if fbstatus.isCrosspostEnabled() %}
+ <form action="/settings" method="post">
+ <div>
+ Facebook: <b>Enabled</b> &mdash;
+ <input type="hidden" name="page" value="facebook-disable"/>
+ <input type="submit" value=" Disable "/>
+ </div>
+ </form>
+ {% else %}
+ <form action="/settings" method="post">
+ <div>
+ Facebook: <b>Disabled</b> &mdash;
+ <input type="hidden" name="page" value="facebook-enable"/>
+ <input type="submit" value=" Enable "/>
+ </div>
+ </form>
+ {% endif %}
+ {% else %}
+ <p>Cross-posting to Facebook: <a href="/_fblogin"><img src="//static.juick.com/facebook-connect.png" alt="Connect to Facebook"/></a></p>
+ {% endif %}
+ </fieldset>
+ <fieldset>
+ <legend style="background: url(//static.juick.com/settings/twitter.png) no-repeat; padding-left: 58px; line-height: 48px;">
+ Twitter</legend>
+ {% if twitter_name is not empty %}
+ <form action="/settings" method="post">
+ <div>Twitter: <b>{{ twitter_name }}</b> &mdash;
+ <input type="hidden" name="page" value="twitter-del"/>
+ <input type="submit" value=" Disable "/>
+ </div>
+ </form>
+ {% else %}
+ <p>Cross-posting to Twitter: <a href="/_twitter"><img src="//static.juick.com/twitter-connect.png"
+ alt="Connect to Twitter"/></a></p>
+ {% endif %}
+ </fieldset>
+
+ </article>
+</section>
+<aside id="column">
+ <p th:replace="views/partial/settings/tabs">Settings navigation</p>
+</aside>
+
+</body>
+</html> \ No newline at end of file
diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_password.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_password.html
new file mode 100644
index 00000000..4c6e0d4d
--- /dev/null
+++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_password.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org"
+ xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+ layout:decorate="~{layout/mainLayout}">
+<head>
+ <title th:text="${title}">Settings title</title>
+</head>
+
+<body>
+<section id="content">
+ <fieldset>
+ <legend>Changing your password</legend>
+ <form action="/settings" method="post">
+ <input type="hidden" name="page" value="password"/>
+ <p>Change password: <input type="password" name="password" size="8"/> <input type="submit"
+ value=" Update "/><br/>
+ <i>(max. length - 16 symbols)</i></p>
+ </form>
+ </fieldset>
+</section>
+<aside id="column">
+ <p th:replace="views/partial/settings/tabs">Settings navigation</p>
+</aside>
+
+</body>
+</html> \ No newline at end of file
diff --git a/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_result.html b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_result.html
new file mode 100644
index 00000000..ddde1baf
--- /dev/null
+++ b/juick-spring-www/src/main/webapp/WEB-INF/templates/views/settings_result.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html xmlns:th="http://www.thymeleaf.org"
+ xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
+ layout:decorate="~{layout/mainLayout}">
+<head>
+ <title th:text="${title}">Settings title</title>
+</head>
+
+<body>
+<section id="content">
+ <p th:utext="${result}">Settings update status</p>
+</section>
+<aside id="column">
+ <p th:replace="views/partial/settings/tabs">Settings navigation</p>
+</aside>
+
+</body>
+</html> \ No newline at end of file