Repository: incubator-freemarker Updated Branches: refs/heads/3 d943ba431 -> 04c0f1a54
FREEMARKER-55: Adding form.label 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/04c0f1a5 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/04c0f1a5 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/04c0f1a5 Branch: refs/heads/3 Commit: 04c0f1a5459a3e7df07c6aae6d4447fd1bd27a0b Parents: d943ba4 Author: Woonsan Ko <woon...@apache.org> Authored: Fri Jan 26 15:44:44 2018 -0500 Committer: Woonsan Ko <woon...@apache.org> Committed: Fri Jan 26 15:44:44 2018 -0500 ---------------------------------------------------------------------- FM3-CHANGE-LOG.txt | 3 + .../model/form/LabelTemplateDirectiveModel.java | 145 +++++++++++++++++++ .../SpringFormTemplateCallableHashModel.java | 1 + .../form/LabelTemplateDirectiveModelTest.java | 71 +++++++++ .../test/model/form/label-directive-usages.ftlh | 53 +++++++ 5 files changed, 273 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/04c0f1a5/FM3-CHANGE-LOG.txt ---------------------------------------------------------------------- diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt index 81d3f6f..078c416 100644 --- a/FM3-CHANGE-LOG.txt +++ b/FM3-CHANGE-LOG.txt @@ -526,6 +526,9 @@ models by default like FreemarkerServlet does. - <form:input ... /> : Replaced by <@form.input ... /> directive. - <form:password ... /> : Replaced by <@form.password ... /> directive. - <form:textarea ... /> : Replaced by <@form.textarea ... /> directive. + - <form:hidden ... /> : Replaced by <@form.hidden ... /> directive. + - <form:button ... /> : Replaced by <@form.button ... /> directive. + - <form:label ... /> : Replaced by <@form.label ... /> directive. Core / Miscellaneous .................... http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/04c0f1a5/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/LabelTemplateDirectiveModel.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/LabelTemplateDirectiveModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/LabelTemplateDirectiveModel.java new file mode 100644 index 0000000..efb635c --- /dev/null +++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/LabelTemplateDirectiveModel.java @@ -0,0 +1,145 @@ +/* + * 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.util.StringUtils; +import org.springframework.web.servlet.support.RequestContext; + +/** + * Provides <code>TemplateModel</code> for data-binding-aware HTML '{@code label}' 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.label 'user.email'>E-Mail:</@form.label> + * <@form.input 'user.email' /> + * </PRE> + * <P> + * <EM>Note:</EM> Unlike Spring Framework's <code><form:label /></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 LabelTemplateDirectiveModel extends AbstractHtmlElementTemplateDirectiveModel { + + public static final String NAME = "label"; + + private static final String FOR_ATTRIBUTE = "for"; + + private static final int NAMED_ARGS_OFFSET = AbstractHtmlElementTemplateDirectiveModel.ARGS_LAYOUT + .getPredefinedNamedArgumentsEndIndex(); + + private static final int FOR_ID_PARAM_IDX = NAMED_ARGS_OFFSET; + private static final String FOR_ID_PARAM_NAME = FOR_ATTRIBUTE; + + protected static final ArgumentArrayLayout ARGS_LAYOUT = + ArgumentArrayLayout.create( + 1, + false, + StringToIndexMap.of(AbstractHtmlElementTemplateDirectiveModel.ARGS_LAYOUT.getPredefinedNamedArgumentsMap(), + new StringToIndexMap.Entry(FOR_ID_PARAM_NAME, FOR_ID_PARAM_IDX) + ), + true); + + private String forId; + + protected LabelTemplateDirectiveModel(HttpServletRequest request, HttpServletResponse response) { + super(request, response); + } + + @Override + public ArgumentArrayLayout getDirectiveArgumentArrayLayout() { + return ARGS_LAYOUT; + } + + @Override + public boolean isNestedContentSupported() { + return true; + } + + @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); + + forId = CallableUtils.getOptionalStringArgument(args, FOR_ID_PARAM_IDX, this); + + TagOutputter tagOut = new TagOutputter(out); + + tagOut.beginTag(NAME); + tagOut.writeAttribute(FOR_ATTRIBUTE, resolveFor()); + writeDefaultAttributes(tagOut); + + tagOut.forceBlock(); + + try { + callPlace.executeNestedContent(null, out, env); + } finally { + tagOut.endTag(); + } + } + + public String getForId() { + return forId; + } + + @Override + protected String getName() throws TemplateException { + // suppress the 'id' and 'name' attribute, which is fine for a <label/>. + return null; + } + + protected String resolveFor() throws TemplateException { + if (StringUtils.hasText(forId)) { + return getDisplayString(evaluate(FOR_ATTRIBUTE, forId)); + } else { + return autogenerateFor(); + } + } + + protected String autogenerateFor() throws TemplateException { + return StringUtils.deleteAny(getPropertyPath(), "[]"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/04c0f1a5/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 9fe8062..dc82a41 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 @@ -51,6 +51,7 @@ public final class SpringFormTemplateCallableHashModel implements TemplateHashMo modelsMap.put(HiddenInputTemplateDirectiveModel.NAME, new HiddenInputTemplateDirectiveModel(request, response)); modelsMap.put(TextareaTemplateDirectiveModel.NAME, new TextareaTemplateDirectiveModel(request, response)); modelsMap.put(ButtonTemplateDirectiveModel.NAME, new ButtonTemplateDirectiveModel(request, response)); + modelsMap.put(LabelTemplateDirectiveModel.NAME, new LabelTemplateDirectiveModel(request, response)); } @Override http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/04c0f1a5/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/LabelTemplateDirectiveModelTest.java ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/LabelTemplateDirectiveModelTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/LabelTemplateDirectiveModelTest.java new file mode 100644 index 0000000..86160e3 --- /dev/null +++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/LabelTemplateDirectiveModelTest.java @@ -0,0 +1,71 @@ +/* + * 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.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 LabelTemplateDirectiveModelTest { + + @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/label-directive-usages") + .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print()) + .andExpect(xpath("//form[@id='form1']//label[@for='email']/text()").string("E-Mail:")) + .andExpect(xpath("//form[@id='form2']//label[@for='customEmailId']/text()").string("E-Mail:")) + .andExpect(xpath("//form[@id='form2']//input[@id='customEmailId']/@value").string(user.getEmail())); + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/04c0f1a5/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/label-directive-usages.ftlh ---------------------------------------------------------------------- diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/label-directive-usages.ftlh b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/label-directive-usages.ftlh new file mode 100644 index 0000000..d046962 --- /dev/null +++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/label-directive-usages.ftlh @@ -0,0 +1,53 @@ +<#-- + 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> + <@form.label 'user.email'>E-Mail:</@form.label> + </th> + <td> + <@form.input 'user.email' /> + </td> + </tr> + </table> + </form> + + <h1>Form 2</h1> + <hr/> + <form id="form2"> + <table> + <tr> + <th> + <@form.label 'user.email' for="customEmailId">E-Mail:</@form.label> + </th> + <td> + <@form.input 'user.email' id="customEmailId"/> + </td> + </tr> + </table> + </form> + +</body> +</html>