You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by wo...@apache.org on 2018/01/25 02:31:41 UTC

incubator-freemarker git commit: FREEMARKER-55: Adding form.button directive

Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 6bfbfd381 -> d943ba431


FREEMARKER-55: Adding form.button 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/d943ba43
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/d943ba43
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/d943ba43

Branch: refs/heads/3
Commit: d943ba43199128592f7d8d89ef5cfbb2edfe93c1
Parents: 6bfbfd3
Author: Woonsan Ko <wo...@apache.org>
Authored: Wed Jan 24 21:31:34 2018 -0500
Committer: Woonsan Ko <wo...@apache.org>
Committed: Wed Jan 24 21:31:34 2018 -0500

----------------------------------------------------------------------
 ...aBoundFormElementTemplateDirectiveModel.java |   4 +-
 ...stractHtmlElementTemplateDirectiveModel.java |   5 +-
 .../form/ButtonTemplateDirectiveModel.java      | 163 +++++++++++++++++++
 .../SpringFormTemplateCallableHashModel.java    |   1 +
 .../form/ButtonTemplateDirectiveModelTest.java  |  73 +++++++++
 .../model/form/button-directive-usages.ftlh     |  39 +++++
 .../form/hidden-input-directive-usages.ftlh     |   2 +-
 7 files changed, 284 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d943ba43/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractDataBoundFormElementTemplateDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractDataBoundFormElementTemplateDirectiveModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractDataBoundFormElementTemplateDirectiveModel.java
index faa8e78..8867be0 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractDataBoundFormElementTemplateDirectiveModel.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractDataBoundFormElementTemplateDirectiveModel.java
@@ -92,7 +92,9 @@ abstract class AbstractDataBoundFormElementTemplateDirectiveModel extends Abstra
         path = CallableUtils.getOptionalStringArgument(args, PATH_PARAM_IDX, this);
         id = CallableUtils.getOptionalStringArgument(args, ID_PARAM_IDX, this);
 
-        bindStatus = getBindStatus(env, objectWrapperAndUnwrapper, requestContext, path, false);
+        if (path != null) {
+            bindStatus = getBindStatus(env, objectWrapperAndUnwrapper, requestContext, path, false);
+        }
     }
 
     protected BindStatus getBindStatus() {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d943ba43/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractHtmlElementTemplateDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractHtmlElementTemplateDirectiveModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractHtmlElementTemplateDirectiveModel.java
index 0633fb8..8c072bc 100644
--- a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractHtmlElementTemplateDirectiveModel.java
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/AbstractHtmlElementTemplateDirectiveModel.java
@@ -40,6 +40,7 @@ import org.apache.freemarker.core.util.CallableUtils;
 import org.apache.freemarker.core.util.StringToIndexMap;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
+import org.springframework.web.servlet.support.BindStatus;
 import org.springframework.web.servlet.support.RequestContext;
 
 /**
@@ -337,7 +338,9 @@ abstract class AbstractHtmlElementTemplateDirectiveModel
     }
 
     protected String resolveCssClass() throws TemplateException {
-        if (getBindStatus().isError() && StringUtils.hasText(getCssErrorClass())) {
+        final BindStatus bindStatus = getBindStatus();
+
+        if (bindStatus != null && bindStatus.isError() && StringUtils.hasText(getCssErrorClass())) {
             return ObjectUtils.getDisplayString(evaluate("cssErrorClass", getCssErrorClass()));
         } else {
             return ObjectUtils.getDisplayString(evaluate("cssClass", getCssClass()));

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d943ba43/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/ButtonTemplateDirectiveModel.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/ButtonTemplateDirectiveModel.java b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/ButtonTemplateDirectiveModel.java
new file mode 100644
index 0000000..9fa9c1f
--- /dev/null
+++ b/freemarker-spring/src/main/java/org/apache/freemarker/spring/model/form/ButtonTemplateDirectiveModel.java
@@ -0,0 +1,163 @@
+/*
+ * 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 button}' element.
+ * This tag is provided for completeness if the application relies on a
+ * <code>org.springframework.web.servlet.support.RequestDataValueProcessor</code>.
+ * <P>
+ * This directive supports the following parameters:
+ * <UL>
+ * <LI>
+ *   ... TODO ...
+ * </LI>
+ * </UL>
+ * </P>
+ * <P>
+ * Some valid example(s):
+ * </P>
+ * <PRE>
+ *   &lt;@form.button 'user.email' /&gt;
+ * </PRE>
+ * <P>
+ * <EM>Note:</EM> Unlike Spring Framework's <code>&lt;form:button /&gt;</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 ButtonTemplateDirectiveModel extends AbstractHtmlElementTemplateDirectiveModel {
+
+    public static final String NAME = "button";
+
+    private static final int NAMED_ARGS_OFFSET = AbstractHtmlElementTemplateDirectiveModel.ARGS_LAYOUT
+            .getPredefinedNamedArgumentsEndIndex();
+
+    private static final int NAME_PARAM_IDX = NAMED_ARGS_OFFSET;
+    private static final String NAME_PARAM_NAME = "name";
+
+    private static final int VALUE_PARAM_IDX = NAMED_ARGS_OFFSET + 1;
+    private static final String VALUE_PARAM_NAME = "value";
+
+    private static final int DISABLED_PARAM_IDX = NAMED_ARGS_OFFSET + 2;
+    private static final String DISABLED_PARAM_NAME = "disabled";
+
+    protected static final ArgumentArrayLayout ARGS_LAYOUT =
+            ArgumentArrayLayout.create(
+                    1,
+                    false,
+                    StringToIndexMap.of(AbstractHtmlElementTemplateDirectiveModel.ARGS_LAYOUT.getPredefinedNamedArgumentsMap(),
+                            new StringToIndexMap.Entry(NAME_PARAM_NAME, NAME_PARAM_IDX),
+                            new StringToIndexMap.Entry(VALUE_PARAM_NAME, VALUE_PARAM_IDX),
+                            new StringToIndexMap.Entry(DISABLED_PARAM_NAME, DISABLED_PARAM_IDX)
+                            ),
+                    true);
+
+    private String name;
+    private String value;
+    private boolean disabled;
+
+    protected ButtonTemplateDirectiveModel(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);
+
+        name = CallableUtils.getOptionalStringArgument(args, NAME_PARAM_IDX, this);
+        value = CallableUtils.getOptionalStringArgument(args, VALUE_PARAM_IDX, this);
+        disabled = CallableUtils.getOptionalBooleanArgument(args, DISABLED_PARAM_IDX, this, false);
+
+        TagOutputter tagOut = new TagOutputter(out);
+
+        tagOut.beginTag("button");
+
+        writeDefaultAttributes(tagOut);
+
+        // more optional attributes by this tag
+        tagOut.writeAttribute("type", getType());
+
+        String valueToUse = (getValue() != null) ? getValue() : getDefaultValue();
+        tagOut.writeAttribute("value", processFieldValue(env, getName(), valueToUse, getType()));
+
+        if (isDisabled()) {
+            tagOut.writeAttribute(DISABLED_PARAM_NAME, "disabled");
+        }
+
+        tagOut.forceBlock();
+
+        try {
+            callPlace.executeNestedContent(null, out, env);
+        } finally {
+            tagOut.endTag();
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public boolean isDisabled() {
+        return disabled;
+    }
+
+    protected String getDefaultValue() {
+        return "Submit";
+    }
+
+    protected String getType() {
+        return "submit";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d943ba43/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 11d5e33..9fe8062 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
@@ -50,6 +50,7 @@ public final class SpringFormTemplateCallableHashModel implements TemplateHashMo
         modelsMap.put(PasswordInputTemplateDirectiveModel.NAME, new PasswordInputTemplateDirectiveModel(request, response));
         modelsMap.put(HiddenInputTemplateDirectiveModel.NAME, new HiddenInputTemplateDirectiveModel(request, response));
         modelsMap.put(TextareaTemplateDirectiveModel.NAME, new TextareaTemplateDirectiveModel(request, response));
+        modelsMap.put(ButtonTemplateDirectiveModel.NAME, new ButtonTemplateDirectiveModel(request, response));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d943ba43/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/ButtonTemplateDirectiveModelTest.java
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/ButtonTemplateDirectiveModelTest.java b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/ButtonTemplateDirectiveModelTest.java
new file mode 100644
index 0000000..4d6b871
--- /dev/null
+++ b/freemarker-spring/src/test/java/org/apache/freemarker/spring/model/form/ButtonTemplateDirectiveModelTest.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.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 ButtonTemplateDirectiveModelTest {
+
+    @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();
+        mockMvc.perform(get("/users/{userId}/", userId).param("viewName", "test/model/form/button-directive-usages")
+                .accept(MediaType.parseMediaType("text/html"))).andExpect(status().isOk())
+                .andExpect(content().contentTypeCompatibleWith("text/html")).andDo(print())
+                .andExpect(xpath("//form[@id='form1']//button[@name='button1']/@value").string("Submit"))
+                .andExpect(xpath("//form[@id='form1']//button[@name='button1']/text()")
+                        .string(Matchers.equalToIgnoringWhiteSpace("Something inside...")))
+                .andExpect(xpath("//form[@id='form1']//button[@name='button2']/@value").string("Button 2"))
+                .andExpect(xpath("//form[@id='form1']//button[@name='button3']/@disabled").string("disabled"))
+                .andExpect(xpath("//form[@id='form1']//button[@name='button3']/@value").string("Button 3"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d943ba43/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/button-directive-usages.ftlh
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/button-directive-usages.ftlh b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/button-directive-usages.ftlh
new file mode 100644
index 0000000..860af4e
--- /dev/null
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/button-directive-usages.ftlh
@@ -0,0 +1,39 @@
+<#--
+  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>
+        <td>
+          <@form.button name="button1">
+            Something inside...
+          </...@form.button>
+          <@form.button name="button2" value="Button 2" />
+          <@form.button name="button3" value="Button 3" disabled=true />
+        </td>
+      </tr>
+    </table>
+  </form>
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d943ba43/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/hidden-input-directive-usages.ftlh
----------------------------------------------------------------------
diff --git a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/hidden-input-directive-usages.ftlh b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/hidden-input-directive-usages.ftlh
index 633956d..090ebb4 100644
--- a/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/hidden-input-directive-usages.ftlh
+++ b/freemarker-spring/src/test/resources/META-INF/web-resources/views/test/model/form/hidden-input-directive-usages.ftlh
@@ -24,7 +24,7 @@
   <form id="form1">
     <table>
       <tr>
-        <th>Hidden1 :</th>
+        <th>Hidden 1:</th>
         <td>
           <@form.hidden 'user.email' />
         </td>