Repository: incubator-freemarker Updated Branches: refs/heads/3 3ec52aaa0 -> c38255647
FREEMARKER-55: Adding form.textarea directive Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/c3825564 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/c3825564 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/c3825564 Branch: refs/heads/3 Commit: c38255647f343af1a4467970d407f345c83d9f25 Parents: 3ec52aa Author: Woonsan Ko <woon...@apache.org> Authored: Wed Jan 24 20:31:43 2018 -0500 Committer: Woonsan Ko <woon...@apache.org> Committed: Wed Jan 24 20:31:43 2018 -0500 ---------------------------------------------------------------------- FM3-CHANGE-LOG.txt | 1 + .../SpringFormTemplateCallableHashModel.java | 1 + .../form/TextareaTemplateDirectiveModel.java | 147 +++++++++++++++++++ .../spring/example/mvc/users/User.java | 11 +- .../example/mvc/users/UserRepository.java | 5 + .../TextareaTemplateDirectiveModelTest.java | 73 +++++++++ .../model/form/textarea-directive-usages.ftlh | 36 +++++ 7 files changed, 273 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c3825564/FM3-CHANGE-LOG.txt ---------------------------------------------------------------------- diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt index 30a86c6..81d3f6f 100644 --- a/FM3-CHANGE-LOG.txt +++ b/FM3-CHANGE-LOG.txt @@ -525,6 +525,7 @@ models by default like FreemarkerServlet does. - <form:form ... /> : Replaced by <@form.form ... /> directive. - <form:input ... /> : Replaced by <@form.input ... /> directive. - <form:password ... /> : Replaced by <@form.password ... /> directive. + - <form:textarea ... /> : Replaced by <@form.textarea ... /> directive. Core / Miscellaneous .................... http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c3825564/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java index dde548a..eefc9cc 100644 --- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/SpringFormTemplateCallableHashModel.java @@ -48,6 +48,7 @@ public final class SpringFormTemplateCallableHashModel implements TemplateHashMo modelsMap.put(FormTemplateDirectiveModel.NAME, new FormTemplateDirectiveModel(request, response)); modelsMap.put(InputTemplateDirectiveModel.NAME, new InputTemplateDirectiveModel(request, response)); modelsMap.put(PasswordInputTemplateDirectiveModel.NAME, new PasswordInputTemplateDirectiveModel(request, response)); + modelsMap.put(TextareaTemplateDirectiveModel.NAME, new TextareaTemplateDirectiveModel(request, response)); } @Override http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c3825564/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/TextareaTemplateDirectiveModel.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/TextareaTemplateDirectiveModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/TextareaTemplateDirectiveModel.java new file mode 100644 index 0000000..0b4fb85 --- /dev/null +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/TextareaTemplateDirectiveModel.java @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.freemarker.spring.model.form; + +import java.io.IOException; +import java.io.Writer; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.freemarker.core.CallPlace; +import org.apache.freemarker.core.Environment; +import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core.model.ArgumentArrayLayout; +import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.util.CallableUtils; +import org.apache.freemarker.core.util.StringToIndexMap; +import org.springframework.web.servlet.support.RequestContext; + +/** + * Provides <code>TemplateModel</code> for data-binding-aware HTML '{@code textarea}' element. + * <P> + * This directive supports the following parameters: + * <UL> + * <LI><code>path</code>: The first positional parameter pointing to the bean or bean property to bind status information for.</LI> + * <LI> + * ... TODO ... + * </LI> + * </UL> + * </P> + * <P> + * Some valid example(s): + * </P> + * <PRE> + * <@form.textarea 'user.description' rows="10" cols="80" /> + * </PRE> + * <P> + * <EM>Note:</EM> Unlike Spring Framework's <code><form:input /></code> JSP Tag Library, this directive + * does not support <code>htmlEscape</code> parameter. It always renders HTML's without escaping + * because it is much easier to control escaping in FreeMarker Template expressions. + * </P> + */ +class TextareaTemplateDirectiveModel extends AbstractHtmlInputElementTemplateDirectiveModel { + + public static final String NAME = "textarea"; + + private static final int NAMED_ARGS_OFFSET = AbstractHtmlInputElementTemplateDirectiveModel.ARGS_LAYOUT + .getPredefinedNamedArgumentsEndIndex(); + + private static final int ROWS_PARAM_IDX = NAMED_ARGS_OFFSET; + private static final String ROWS_PARAM_NAME = "rows"; + + private static final int COLS_PARAM_IDX = NAMED_ARGS_OFFSET + 1; + private static final String COLS_PARAM_NAME = "cols"; + + private static final int ONSELECT_PARAM_IDX = NAMED_ARGS_OFFSET + 2; + private static final String ONSELECT_PARAM_NAME = "onselect"; + + protected static final ArgumentArrayLayout ARGS_LAYOUT = + ArgumentArrayLayout.create( + 1, + false, + StringToIndexMap.of(AbstractHtmlInputElementTemplateDirectiveModel.ARGS_LAYOUT.getPredefinedNamedArgumentsMap(), + new StringToIndexMap.Entry(ROWS_PARAM_NAME, ROWS_PARAM_IDX), + new StringToIndexMap.Entry(COLS_PARAM_NAME, COLS_PARAM_IDX), + new StringToIndexMap.Entry(ONSELECT_PARAM_NAME, ONSELECT_PARAM_IDX) + ), + true + ); + + private String rows; + private String cols; + private String onselect; + + protected TextareaTemplateDirectiveModel(HttpServletRequest request, HttpServletResponse response) { + super(request, response); + } + + @Override + public boolean isNestedContentSupported() { + return false; + } + + @Override + public ArgumentArrayLayout getDirectiveArgumentArrayLayout() { + return ARGS_LAYOUT; + } + + @Override + protected void executeInternal(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env, + ObjectWrapperAndUnwrapper objectWrapperAndUnwrapper, RequestContext requestContext) + throws TemplateException, IOException { + + super.executeInternal(args, callPlace, out, env, objectWrapperAndUnwrapper, requestContext); + + rows = CallableUtils.getOptionalStringArgument(args, ROWS_PARAM_IDX, this); + cols = CallableUtils.getOptionalStringArgument(args, COLS_PARAM_IDX, this); + onselect = CallableUtils.getOptionalStringArgument(args, ONSELECT_PARAM_IDX, this); + + TagOutputter tagOut = new TagOutputter(out); + + tagOut.beginTag(NAME); + + writeDefaultAttributes(tagOut); + + // more optional attributes by this tag + writeOptionalAttribute(tagOut, ROWS_PARAM_NAME, getRows()); + writeOptionalAttribute(tagOut, COLS_PARAM_NAME, getCols()); + writeOptionalAttribute(tagOut, ONSELECT_PARAM_NAME, getOnselect()); + + String value = getDisplayString(getBindStatus().getValue(), getBindStatus().getEditor()); + tagOut.appendValue("\r\n" + processFieldValue(env, getName(), value, "textarea")); + + tagOut.endTag(); + } + + public String getRows() { + return rows; + } + + public String getCols() { + return cols; + } + + public String getOnselect() { + return onselect; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c3825564/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java index 41b42c4..c1a77f0 100644 --- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java +++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/User.java @@ -29,6 +29,7 @@ public class User { private String firstName; private String lastName; private Date birthDate; + private String description; public User() { } @@ -81,9 +82,17 @@ public class User { this.birthDate = birthDate; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + @Override public String toString() { return super.toString() + " {id=" + id + ", firstName='" + firstName + "', lastName='" + lastName + "', email='" - + email + "', birthDate='" + birthDate + "'}"; + + email + "', birthDate='" + birthDate + "', description='" + description + "'}"; } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c3825564/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserRepository.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserRepository.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserRepository.java index 933b1a0..73c8d4d 100644 --- a/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserRepository.java +++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/example/mvc/users/UserRepository.java @@ -43,6 +43,8 @@ public class UserRepository { birthDate.set(Calendar.MONTH, Calendar.JANUARY); birthDate.set(Calendar.DATE, 5); user.setBirthDate(birthDate.getTime()); + user.setDescription("Lorem ipsum dolor sit amet, \r\nconsectetur adipiscing elit, \r\n" + + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."); usersMap.put(id, user); id = 102L; @@ -56,6 +58,8 @@ public class UserRepository { birthDate.set(Calendar.MONTH, Calendar.FEBRUARY); birthDate.set(Calendar.DATE, 7); user.setBirthDate(birthDate.getTime()); + user.setDescription("Ut enim ad minim veniam, \r\n" + + "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."); usersMap.put(id, user); } @@ -100,6 +104,7 @@ public class UserRepository { clone.setFirstName(source.getFirstName()); clone.setLastName(source.getLastName()); clone.setBirthDate(source.getBirthDate()); + clone.setDescription(source.getDescription()); return clone; } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c3825564/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/TextareaTemplateDirectiveModelTest.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/TextareaTemplateDirectiveModelTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/TextareaTemplateDirectiveModelTest.java new file mode 100644 index 0000000..5823e73 --- /dev/null +++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/TextareaTemplateDirectiveModelTest.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.freemarker.spring.model.form; + +import org.apache.freemarker.spring.example.mvc.users.User; +import org.apache.freemarker.spring.example.mvc.users.UserRepository; +import org.hamcrest.Matchers; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath; + +@RunWith(SpringJUnit4ClassRunner.class) +@WebAppConfiguration("classpath:META-INF/web-resources") +@ContextConfiguration(locations = { "classpath:org/apache/freemarker/spring/example/mvc/users/users-mvc-context.xml" }) +public class TextareaTemplateDirectiveModelTest { + + @Autowired + private WebApplicationContext wac; + + @Autowired + private UserRepository userRepository; + + private MockMvc mockMvc; + + @Before + public void setUp() { + mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); + } + + @Test + public void testBasicUsages() throws Exception { + final Long userId = userRepository.getUserIds().iterator().next(); + final User user = userRepository.getUser(userId); + mockMvc.perform(get("/users/{userId}/", userId).param("viewName", "test/model/form/textarea-directive-usages") + .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print()) + .andExpect(xpath("//form[@id='form1']//textarea[@name='description']/@rows").string("10")) + .andExpect(xpath("//form[@id='form1']//textarea[@name='description']/@cols").string("80")) + .andExpect(xpath("//form[@id='form1']//textarea[@name='description']/text()") + .string(Matchers.equalToIgnoringWhiteSpace(user.getDescription()))); + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c3825564/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/textarea-directive-usages.ftlh ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/textarea-directive-usages.ftlh b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/textarea-directive-usages.ftlh new file mode 100644 index 0000000..944e278 --- /dev/null +++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/textarea-directive-usages.ftlh @@ -0,0 +1,36 @@ +<#-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> +<html> +<body> + + <h1>Form 1</h1> + <hr/> + <form id="form1"> + <table> + <tr> + <th>Description:</th> + <td> + <@form.textarea 'user.description' rows="10" cols="80" /> + </td> + </tr> + </table> + </form> + +</body> +</html>