#if($permissionTool.hasPermission($targedId.longValue(),"BRANCH","BranchPermission.MOVE_TOPICS"))
$user.username
#end
diff --git a/jcommune-view/jcommune-web-controller/src/test/java/org/jtalks/jcommune/web/dto/json/FailJsonResponseTest.java b/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/dto/json/FailJsonResponseTest.java
similarity index 86%
rename from jcommune-view/jcommune-web-controller/src/test/java/org/jtalks/jcommune/web/dto/json/FailJsonResponseTest.java
rename to jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/dto/json/FailJsonResponseTest.java
index 17bfe70373..f322f86fd8 100644
--- a/jcommune-view/jcommune-web-controller/src/test/java/org/jtalks/jcommune/web/dto/json/FailJsonResponseTest.java
+++ b/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/dto/json/FailJsonResponseTest.java
@@ -12,10 +12,14 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-package org.jtalks.jcommune.web.dto.json;
+package org.jtalks.jcommune.plugin.api.dto.json;
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertEquals;
+
+import org.jtalks.jcommune.plugin.api.web.dto.json.FailJsonResponse;
+import org.jtalks.jcommune.plugin.api.web.dto.json.JsonResponseReason;
+import org.jtalks.jcommune.plugin.api.web.dto.json.JsonResponseStatus;
import org.testng.annotations.Test;
public class FailJsonResponseTest {
diff --git a/jcommune-view/jcommune-web-controller/src/test/java/org/jtalks/jcommune/web/dto/json/FailValidationJsonResponseTest.java b/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/dto/json/FailValidationJsonResponseTest.java
similarity index 84%
rename from jcommune-view/jcommune-web-controller/src/test/java/org/jtalks/jcommune/web/dto/json/FailValidationJsonResponseTest.java
rename to jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/dto/json/FailValidationJsonResponseTest.java
index a7b175137f..37d953a3e6 100644
--- a/jcommune-view/jcommune-web-controller/src/test/java/org/jtalks/jcommune/web/dto/json/FailValidationJsonResponseTest.java
+++ b/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/dto/json/FailValidationJsonResponseTest.java
@@ -12,12 +12,16 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-package org.jtalks.jcommune.web.dto.json;
+package org.jtalks.jcommune.plugin.api.dto.json;
import static org.testng.AssertJUnit.assertEquals;
import java.util.ArrayList;
import java.util.List;
+import org.jtalks.jcommune.plugin.api.web.dto.json.FailValidationJsonResponse;
+import org.jtalks.jcommune.plugin.api.web.dto.json.JsonResponseReason;
+import org.jtalks.jcommune.plugin.api.web.dto.json.JsonResponseStatus;
+import org.jtalks.jcommune.plugin.api.web.dto.json.ValidationError;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.testng.annotations.Test;
@@ -32,7 +36,7 @@ public class FailValidationJsonResponseTest {
@SuppressWarnings("unchecked")
@Test
public void testConstructorWithBindingResult() {
- List allErrors = new ArrayList();
+ List allErrors = new ArrayList<>();
allErrors.add(new ObjectError(OBJECT_NAME, OBJECT_MESSAGE));
allErrors.add(new FieldError(OBJECT_NAME, FIELD, FIELD_MESSAGE));
diff --git a/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/filters/TopicTypeFilterTest.java b/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/filters/TopicTypeFilterTest.java
new file mode 100644
index 0000000000..4eb27f38b6
--- /dev/null
+++ b/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/filters/TopicTypeFilterTest.java
@@ -0,0 +1,78 @@
+/**
+ * Copyright (C) 2011 JTalks.org Team
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+package org.jtalks.jcommune.plugin.api.filters;
+
+import org.jtalks.jcommune.plugin.api.core.Plugin;
+import org.jtalks.jcommune.plugin.api.core.TopicPlugin;
+import org.testng.annotations.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * @author Dmitry S. Dolzhenko
+ */
+public class TopicTypeFilterTest {
+ @Test
+ public void filterShouldAcceptTopicPluginWithCorrectTopicType() throws Exception {
+ TopicPlugin topicPlugin = getPluginWithTopicType("type");
+
+ TopicTypeFilter topicTypeFilter = new TopicTypeFilter("type");
+ assertTrue(topicTypeFilter.accept(topicPlugin));
+ }
+
+ @Test
+ public void filterShouldRejectTopicPluginWithIncorrectTopicType() throws Exception {
+ TopicPlugin topicPlugin = getPluginWithTopicType("type");
+
+ TopicTypeFilter topicTypeFilter = new TopicTypeFilter("type1");
+ assertFalse(topicTypeFilter.accept(topicPlugin));
+ }
+
+ @Test
+ public void filterShouldRejectNotTopicPlugin() {
+ Plugin plugin = mock(Plugin.class);
+
+ TopicTypeFilter topicTypeFilter = new TopicTypeFilter("type");
+ assertFalse(topicTypeFilter.accept(plugin));
+ }
+
+ @Test
+ public void filterShouldRejectTopicPlugin_IfTopicTypeCaseDoesNotMatch() {
+ TopicPlugin topicPlugin = getPluginWithTopicType("type");
+
+ TopicTypeFilter topicTypeFilter = new TopicTypeFilter("Type");
+ assertFalse(topicTypeFilter.accept(topicPlugin));
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void filterShouldThrowException_IfTopicTypeIsNull() {
+ new TopicTypeFilter(null);
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void filterShouldThrowException_IfTopicTypeIsEmpty() {
+ new TopicTypeFilter("");
+ }
+
+ private TopicPlugin getPluginWithTopicType(String type) {
+ TopicPlugin topicPlugin = mock(TopicPlugin.class);
+ when(topicPlugin.getTopicType()).thenReturn(type);
+
+ return topicPlugin;
+ }
+}
\ No newline at end of file
diff --git a/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/service/nontransactional/PluginLocationServiceImplTest.java b/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/service/nontransactional/PluginLocationServiceImplTest.java
index fefc537071..a8c8a15ca7 100644
--- a/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/service/nontransactional/PluginLocationServiceImplTest.java
+++ b/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/service/nontransactional/PluginLocationServiceImplTest.java
@@ -16,16 +16,15 @@
import org.jtalks.jcommune.model.entity.JCUser;
import org.jtalks.jcommune.model.entity.Topic;
+import org.jtalks.jcommune.model.entity.UserInfo;
import org.jtalks.jcommune.plugin.api.service.PluginLocationService;
import org.mockito.Mock;
-import org.testng.annotations.BeforeMethod;
+import org.mockito.Mockito;
import org.testng.annotations.Test;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
import static org.testng.Assert.assertEquals;
/**
@@ -35,23 +34,15 @@ public class PluginLocationServiceImplTest {
@Mock
private PluginLocationService locationService;
- @BeforeMethod
- public void init() {
- initMocks(this);
- PluginLocationServiceImpl service = (PluginLocationServiceImpl)PluginLocationServiceImpl.getInstance();
- service.setLocationService(locationService);
- }
-
@Test
- public void testGetUsersViewing() {
- JCUser user = new JCUser("name", "mail@example.com", "password");
+ public void userShouldBeInViewersList() {
+ locationService = Mockito.mock(PluginLocationService.class);
+ UserInfo user = new UserInfo(new JCUser("name", "mail@example.com", "password"));
Topic topic = new Topic();
- when(locationService.getUsersViewing(topic)).thenReturn(Arrays.asList(user));
-
- List users = PluginLocationServiceImpl.getInstance().getUsersViewing(topic);
-
+ Mockito.when(locationService.getUsersViewing(topic)).thenReturn(Collections.singletonList(user));
+ ((PluginLocationServiceImpl)PluginLocationServiceImpl.getInstance()).setLocationService(locationService);
+ List users = PluginLocationServiceImpl.getInstance().getUsersViewing(topic);
assertEquals(users.size(), 1);
assertEquals(users.get(0), user);
}
}
-
diff --git a/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/service/transactional/TransactionalPluginLastReadPostServiceTest.java b/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/service/transactional/TransactionalPluginLastReadPostServiceTest.java
index 55f83b1b07..c4e3d21362 100644
--- a/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/service/transactional/TransactionalPluginLastReadPostServiceTest.java
+++ b/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/service/transactional/TransactionalPluginLastReadPostServiceTest.java
@@ -42,8 +42,8 @@ public void init() {
public void markTopicPageAsReadShouldCallLastReadPostService() {
Topic topic = new Topic();
- TransactionalPluginLastReadPostService.getInstance().markTopicPageAsRead(topic, 1);
+ TransactionalPluginLastReadPostService.getInstance().markTopicAsRead(topic);
- verify(lastReadPostService).markTopicPageAsRead(topic, 1);
+ verify(lastReadPostService).markTopicAsRead(topic);
}
}
diff --git a/jcommune-view/jcommune-web-controller/src/test/java/org/jtalks/jcommune/web/locale/JcLocaleResolverTest.java b/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/web/locale/JcLocaleResolverTest.java
similarity index 78%
rename from jcommune-view/jcommune-web-controller/src/test/java/org/jtalks/jcommune/web/locale/JcLocaleResolverTest.java
rename to jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/web/locale/JcLocaleResolverTest.java
index f636dfbbf3..868c82c399 100644
--- a/jcommune-view/jcommune-web-controller/src/test/java/org/jtalks/jcommune/web/locale/JcLocaleResolverTest.java
+++ b/jcommune-plugin-api/src/test/java/org/jtalks/jcommune/plugin/api/web/locale/JcLocaleResolverTest.java
@@ -12,12 +12,12 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-package org.jtalks.jcommune.web.locale;
+package org.jtalks.jcommune.plugin.api.web.locale;
import org.jtalks.jcommune.model.entity.AnonymousUser;
import org.jtalks.jcommune.model.entity.JCUser;
import org.jtalks.jcommune.model.entity.Language;
-import org.jtalks.jcommune.service.UserService;
+import org.jtalks.jcommune.plugin.api.service.UserReader;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -38,23 +38,24 @@
*/
public class JcLocaleResolverTest {
@Mock
- private UserService userService;
+ private UserReader userReader;
@Mock
private HttpServletRequest request;
@BeforeMethod
public void init() {
initMocks(this);
+ JcLocaleResolver resolver = (JcLocaleResolver) JcLocaleResolver.getInstance();
+ resolver.setUserReader(userReader);
}
@Test
public void resolveLocaleShouldReturnUserLocaleIfUserLoggedIn() {
- JcLocaleResolver localeResolver = new JcLocaleResolver(userService);
JCUser currentUser = new JCUser("username", "email@mail.ru", "password");
currentUser.setLanguage(Language.ENGLISH);
- when(userService.getCurrentUser()).thenReturn(currentUser);
+ when(userReader.getCurrentUser()).thenReturn(currentUser);
- Locale result = localeResolver.resolveLocale(request);
+ Locale result = JcLocaleResolver.getInstance().resolveLocale(request);
assertEquals(result, currentUser.getLanguage().getLocale());
verify(request).setAttribute(CookieLocaleResolver.LOCALE_REQUEST_ATTRIBUTE_NAME, Locale.ENGLISH);
@@ -62,14 +63,13 @@ public void resolveLocaleShouldReturnUserLocaleIfUserLoggedIn() {
@Test
public void resolveLocaleShouldReturnRequestLocaleIfUserAnonymous() {
- JcLocaleResolver localeResolver = new JcLocaleResolver(userService);
AnonymousUser user = new AnonymousUser();
- user.setLanguage(Language.SPANISH);
+ user.setLanguage(Language.RUSSIAN);
Locale defaultLocale = Language.ENGLISH.getLocale();
- when(userService.getCurrentUser()).thenReturn(user);
+ when(userReader.getCurrentUser()).thenReturn(user);
when(request.getLocale()).thenReturn(defaultLocale);
- Locale result = localeResolver.resolveLocale(request);
+ Locale result = JcLocaleResolver.getInstance().resolveLocale(request);
assertEquals(result, defaultLocale);
}
@@ -80,13 +80,12 @@ public void resolveLocaleShouldReturnRequestLocaleIfUserAnonymous() {
*/
@Test
public void resolveLocaleShouldNotRetrieveCurrentUserIfRequestLocaleAttributeNotNull() {
- JcLocaleResolver localeResolver = new JcLocaleResolver(userService);
Locale locale = Locale.ENGLISH;
when(request.getAttribute(CookieLocaleResolver.LOCALE_REQUEST_ATTRIBUTE_NAME)).thenReturn(locale);
- Locale result = localeResolver.resolveLocale(request);
+ Locale result = JcLocaleResolver.getInstance().resolveLocale(request);
assertEquals(result, locale);
- verify(userService, never()).getCurrentUser();
+ verify(userReader, never()).getCurrentUser();
}
}
diff --git a/jcommune-plugins/README.md b/jcommune-plugins/README.md
index 8c7c430b6b..5eec25dafd 100644
--- a/jcommune-plugins/README.md
+++ b/jcommune-plugins/README.md
@@ -1,7 +1,17 @@
JCommune Plugins
---
-Plugins are designed to extend the functionality of JCommune forum engine. They can be installed by putting plugin jar file into special folder `JCOMMUNE_PLUGIN_FOLDER` that configured in `$TOMCAT_HOME/conf/Catalina/localhost/jcommune.xml` file.
+Plugins are designed to extend the functionality of JCommune forum engine. They can be installed by putting plugin jar
+file into special folder `JCOMMUNE_PLUGIN_FOLDER` that configured in `$TOMCAT_HOME/conf/Catalina/localhost/jcommune.xml`
+file. By default it points to user home dir. So to install a plugin:
+
+- Download it [from Nexus](http://repo.jtalks.org/content/repositories/builds/org/jtalks/jcommune/)
+ (version should be the same as forum version) or generate its jar file: `mvn package`
+- Copy it from `jcommune-plugins/plugin-name/target/plugin-name*.jar` into `JCOMMUNE_PLUGIN_FOLDER` (by default: `~/`)
+- Log in into forum as admin (by default: admin/admin)
+- Go into Administration -> Plugins and enabled it there.
+
+## Dev Info
Now we provides some velocity macros for plugins:
diff --git a/jcommune-plugins/jcommune-dummy-plugin/pom.xml b/jcommune-plugins/jcommune-dummy-plugin/pom.xml
index 3d73b45042..1e4a6fa59c 100644
--- a/jcommune-plugins/jcommune-dummy-plugin/pom.xml
+++ b/jcommune-plugins/jcommune-dummy-plugin/pom.xml
@@ -5,7 +5,7 @@
jcommune-pluginsorg.jtalks.jcommune
- 2.14-SNAPSHOT
+ 3.13-SNAPSHOTjcommune-dummy-plugin
diff --git a/jcommune-plugins/kaptcha-plugin/pom.xml b/jcommune-plugins/kaptcha-plugin/pom.xml
index c4d95e0d7a..7f2150982e 100644
--- a/jcommune-plugins/kaptcha-plugin/pom.xml
+++ b/jcommune-plugins/kaptcha-plugin/pom.xml
@@ -4,7 +4,7 @@
jcommune-pluginsorg.jtalks.jcommune
- 2.14-SNAPSHOT
+ 3.13-SNAPSHOTkaptcha-plugin
@@ -51,7 +51,7 @@
- maven-assembly-plugin
+ maven-shade-plugin
diff --git a/jcommune-plugins/kaptcha-plugin/src/main/java/org/jtalks/jcommune/plugin/kaptcha/KaptchaPluginService.java b/jcommune-plugins/kaptcha-plugin/src/main/java/org/jtalks/jcommune/plugin/kaptcha/KaptchaPluginService.java
index dc98498fdf..f69c864382 100644
--- a/jcommune-plugins/kaptcha-plugin/src/main/java/org/jtalks/jcommune/plugin/kaptcha/KaptchaPluginService.java
+++ b/jcommune-plugins/kaptcha-plugin/src/main/java/org/jtalks/jcommune/plugin/kaptcha/KaptchaPluginService.java
@@ -22,6 +22,7 @@
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang.StringUtils;
import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.tools.generic.DateTool;
import org.jtalks.jcommune.model.dto.UserDto;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.ui.velocity.VelocityEngineUtils;
@@ -52,6 +53,8 @@ public class KaptchaPluginService {
private static final String BASE_URL = "baseUrl";
private static final String FORM_ELEMENT_ID = "formElementId";
private static final String PLUGIN_PREFIX = "plugin-";
+ private static final String DATE = "date";
+
private Producer captchaProducer;
public KaptchaPluginService(int width, int height, int length, String possibleSymbols) {
@@ -131,6 +134,7 @@ public String getHtml(HttpServletRequest request, String pluginId, Locale locale
model.put(CAPTCHA_PLUGIN_ID, pluginId);
model.put(FORM_ELEMENT_ID, getFormElementId(pluginId));
model.put(BASE_URL, getDeploymentRootUrl(request));
+ model.put(DATE, new DateTool());
return VelocityEngineUtils.mergeTemplateIntoString(
engine, "org/jtalks/jcommune/plugin/kaptcha/template/captcha.vm", "UTF-8", model);
}
diff --git a/jcommune-plugins/kaptcha-plugin/src/main/resources/org/jtalks/jcommune/plugin/kaptcha/template/captcha.vm b/jcommune-plugins/kaptcha-plugin/src/main/resources/org/jtalks/jcommune/plugin/kaptcha/template/captcha.vm
index 36365cee46..7baa3400c3 100644
--- a/jcommune-plugins/kaptcha-plugin/src/main/resources/org/jtalks/jcommune/plugin/kaptcha/template/captcha.vm
+++ b/jcommune-plugins/kaptcha-plugin/src/main/resources/org/jtalks/jcommune/plugin/kaptcha/template/captcha.vm
@@ -16,11 +16,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*#
-
-
+## Fix for [http://jira.jtalks.org/browse/JC-2094] issue. Query t="date" added because Firefox ignores cache control
+## headers if DOM changed by JavaScript and no way to prevent image caching.
+
+## Div can't recieve focus without tabindex attribute.
+
+#end
+
+
+ #set($targedId = ${question.branch.id})
+ #set($canLeavePosts=false)
+ #if((!${question.closed} || ${permissionTool.hasPermission($targedId.longValue(),"BRANCH","BranchPermission.CLOSE_TOPICS")})
+ && ${permissionTool.hasPermission($targedId.longValue(),"BRANCH","BranchPermission.CREATE_POSTS")})
+ #set($canLeavePosts=true)
+ #end
+
+ #if ($canLeavePosts)
+ ## determines if the post editor is show or not - depends on whether user has already left some posts
+ ## Also we shouldn't hide the area if there is some text already (draft or validation error returned after
+ ## submitting invalid form)
+ #set($formVisibilityClass = "")
+ ## determines if the button that prevents users to leave multiple answers is shown
+ #set($antiMultianswerBtnVisibilityClass = "hide-element")
+ #if(${question.topicStarter.id} == ${currentUser.id} && !${postDto.bodyText})
+ #initAnswerConfirmationParams(${messages.getString("label.confirm.self.answer.title")} ${messages.getString("label.confirm.self.answer.message")})
+ #elseif(${question.getUserPostCount(${currentUser})} > 0 && !${postDto.bodyText})
+ #initAnswerConfirmationParams(${messages.getString("label.confirm.multi.answer.title")} ${messages.getString("label.confirm.multi.answer.message")})
+ #end
+ #end
- ${esc.html(${question.title})}
+
+ ${propertiesHolder.allPagesTitlePrefix}
+ ${htmlEscaper.process(${question.title})}
+
- #set($targedId = ${question.branch.id})
- #set($canLeavePosts=false)
- #if((!${question.closed} || ${permissionTool.hasPermission($targedId.longValue(),"BRANCH","BranchPermission.CLOSE_TOPICS")})
- && ${permissionTool.hasPermission($targedId.longValue(),"BRANCH","BranchPermission.CREATE_POSTS")})
- #set($canLeavePosts=true)
- #end
diff --git a/jcommune-view/jcommune-web-view/src/main/webapp/WEB-INF/jsp/userSearch.jsp b/jcommune-view/jcommune-web-view/src/main/webapp/WEB-INF/jsp/userSearch.jsp
new file mode 100644
index 0000000000..9e4758dd59
--- /dev/null
+++ b/jcommune-view/jcommune-web-view/src/main/webapp/WEB-INF/jsp/userSearch.jsp
@@ -0,0 +1,91 @@
+<%--
+
+ Copyright (C) 2011 JTalks.org Team
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
+<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
+<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
+
+
+
+
+
+
+
+
+