This is an automated email from the ASF dual-hosted git repository.

mgrigorov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/wicket.git

commit d22d154ff4d3245df6ecb52cfb00f412b0a25bef
Author: Martin Tzvetanov Grigorov <[email protected]>
AuthorDate: Mon Aug 16 11:43:36 2021 +0300

    WICKET-6914  Visibility change of "File Upload" via ajax causes "missing" 
form-data
    
    Update the <form>'s enctype whenever a multipart form component is 
repainted via Ajax
    
    (cherry picked from commit 06e9f53dbe79a11bf6512263351edc9481dd5619)
---
 .../http/MultipartFormComponentListener.java       | 52 ++++++++++++++
 .../wicket/protocol/http/WebApplication.java       |  6 +-
 .../http/MultipartFormComponentListenerBean.java   | 58 ++++++++++++++++
 .../http/MultipartFormComponentListenerPage.html   | 17 +++++
 .../http/MultipartFormComponentListenerPage.java   | 80 ++++++++++++++++++++++
 .../http/MultipartFormComponentListenerTest.java   | 51 ++++++++++++++
 6 files changed, 262 insertions(+), 2 deletions(-)

diff --git 
a/wicket-core/src/main/java/org/apache/wicket/protocol/http/MultipartFormComponentListener.java
 
b/wicket-core/src/main/java/org/apache/wicket/protocol/http/MultipartFormComponentListener.java
new file mode 100644
index 0000000..f5572ed
--- /dev/null
+++ 
b/wicket-core/src/main/java/org/apache/wicket/protocol/http/MultipartFormComponentListener.java
@@ -0,0 +1,52 @@
+/*
+ * 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.wicket.protocol.http;
+
+import java.util.Map;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.FormComponent;
+import org.apache.wicket.util.visit.IVisitor;
+
+/**
+ * This listener updates the {@link Form}'s <em>enctype</em> whenever a 
multipart {@link FormComponent}
+ * is added to the {@code AjaxRequestTarget}.
+ * This is needed because the multipart form component may change its 
visibility/enablement and thus
+ * change the multipart-ness of the whole form.
+ */
+public class MultipartFormComponentListener implements 
AjaxRequestTarget.IListener
+{
+       static final String ENCTYPE_URL_ENCODED = 
"application/x-www-form-urlencoded";
+
+       @Override
+       public void onBeforeRespond(final Map<String, Component> map, final 
AjaxRequestTarget target)
+       {
+               target.getPage().visitChildren(FormComponent.class, 
(IVisitor<FormComponent<?>, Void>) (formComponent, visit) -> {
+                       if (formComponent.isMultiPart())
+                       {
+                               Form<?> form = formComponent.getForm();
+                               boolean multiPart = form.isMultiPart();
+                               String enctype = multiPart ? 
Form.ENCTYPE_MULTIPART_FORM_DATA : ENCTYPE_URL_ENCODED;
+                               
target.appendJavaScript(String.format("Wicket.$('%s').form.enctype='%s'",
+                                               formComponent.getMarkupId(), 
enctype));
+                               visit.stop();
+                       }
+               });
+       }
+}
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java 
b/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
index 1f4df9d..9825038 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
@@ -758,8 +758,10 @@ public abstract class WebApplication extends Application
                setSessionStoreProvider(HttpSessionStore::new);
                setAjaxRequestTargetProvider(AjaxRequestHandler::new);
 
-               getAjaxRequestTargetListeners().add(new 
AjaxEnclosureListener());
-               
+               AjaxRequestTargetListenerCollection ajaxRequestTargetListeners 
= getAjaxRequestTargetListeners();
+               ajaxRequestTargetListeners.add(new AjaxEnclosureListener());
+               ajaxRequestTargetListeners.add(new 
MultipartFormComponentListener());
+
                // Configure the app.
                configure();
                if (getConfigurationType() == 
RuntimeConfigurationType.DEVELOPMENT)
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/protocol/http/MultipartFormComponentListenerBean.java
 
b/wicket-core/src/test/java/org/apache/wicket/protocol/http/MultipartFormComponentListenerBean.java
new file mode 100644
index 0000000..41b6ca4
--- /dev/null
+++ 
b/wicket-core/src/test/java/org/apache/wicket/protocol/http/MultipartFormComponentListenerBean.java
@@ -0,0 +1,58 @@
+/*
+ * 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.wicket.protocol.http;
+
+import java.io.Serializable;
+
+public class MultipartFormComponentListenerBean implements Serializable {
+       private String textField;
+       private String dropDown;
+
+       /**
+        * @return the textField
+        */
+       public String getTextField() {
+               return textField;
+       }
+
+
+       /**
+        * @return the dropDown
+        */
+       public String getDropDown() {
+               return dropDown;
+       }
+
+
+       /**
+        * @param textField
+        *                     the textField to set
+        */
+       public void setTextField(String textField) {
+               this.textField = textField;
+       }
+
+
+       /**
+        * @param dropDown
+        *                    the dropDown to set
+        */
+       public void setDropDown(String dropDown) {
+               this.dropDown = dropDown;
+       }
+
+}
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/protocol/http/MultipartFormComponentListenerPage.html
 
b/wicket-core/src/test/java/org/apache/wicket/protocol/http/MultipartFormComponentListenerPage.html
new file mode 100644
index 0000000..eb741e8
--- /dev/null
+++ 
b/wicket-core/src/test/java/org/apache/wicket/protocol/http/MultipartFormComponentListenerPage.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html xmlns:wicket="http://wicket.apache.org";>
+<head>
+<meta charset="utf-8" />
+<title>Apache Wicket 6914</title>
+</head>
+<body>
+       <form wicket:id="form">
+               <input type="text" wicket:id="textField" /><br/>
+               <select  wicket:id="dropDown"></select><br/>
+               <input wicket:id="fileUpload" type="file" /><br/> 
+               <input type="submit" value="save" wicket:id="submitButton" 
/><br/><br/><br/>
+       </form>
+       content of textfield or error: <div wicket:id="label"></div>
+       
+</body>
+</html>
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/protocol/http/MultipartFormComponentListenerPage.java
 
b/wicket-core/src/test/java/org/apache/wicket/protocol/http/MultipartFormComponentListenerPage.java
new file mode 100644
index 0000000..d0b7842
--- /dev/null
+++ 
b/wicket-core/src/test/java/org/apache/wicket/protocol/http/MultipartFormComponentListenerPage.java
@@ -0,0 +1,80 @@
+/*
+ * 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.wicket.protocol.http;
+
+import java.util.ArrayList;
+
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Button;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.RequiredTextField;
+import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.Model;
+
+public class MultipartFormComponentListenerPage extends WebPage {
+       private static final long serialVersionUID = 1L;
+
+       public MultipartFormComponentListenerPage() {
+               CompoundPropertyModel<MultipartFormComponentListenerBean> model 
= new CompoundPropertyModel<>(new MultipartFormComponentListenerBean());
+               Form<MultipartFormComponentListenerBean> form = new 
Form<>("form", model);
+               add(form);
+
+               RequiredTextField<String> textField = new 
RequiredTextField<>("textField");
+               form.add(textField);
+
+               ArrayList<String> list = new ArrayList<>();
+               list.add("Option 1");
+               list.add("Option 2");
+
+               FileUploadField fileUpload = new FileUploadField("fileUpload", 
new Model<>(new ArrayList<>()));
+               fileUpload.setOutputMarkupPlaceholderTag(true);
+               form.add(fileUpload);
+
+               DropDownChoice<String> dropDown = new 
DropDownChoice<>("dropDown", list);
+               dropDown.add(new OnChangeAjaxBehavior() {
+
+                       @Override
+                       protected void onUpdate(AjaxRequestTarget target) {
+                               fileUpload.setVisible(!fileUpload.isVisible());
+                               target.add(fileUpload);
+
+                       }
+               });
+               form.add(dropDown);
+
+               final Label label = new Label("label");
+               add(label);
+
+               form.add(new Button("submitButton") {
+                       @Override
+                       public void onSubmit() {
+                               label.setDefaultModel(new 
Model<>(model.getObject().getTextField()));
+                       }
+
+                       @Override
+                       public void onError() {
+                               label.setDefaultModel(new Model<>("Validation 
Error..."));
+                       }
+               });
+
+       }
+}
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/protocol/http/MultipartFormComponentListenerTest.java
 
b/wicket-core/src/test/java/org/apache/wicket/protocol/http/MultipartFormComponentListenerTest.java
new file mode 100644
index 0000000..b5cbf26
--- /dev/null
+++ 
b/wicket-core/src/test/java/org/apache/wicket/protocol/http/MultipartFormComponentListenerTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.wicket.protocol.http;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.util.tester.TagTester;
+import org.apache.wicket.util.tester.WicketTestCase;
+import org.junit.jupiter.api.Test;
+
+/**
+ * https://issues.apache.org/jira/browse/WICKET-6914
+ */
+class MultipartFormComponentListenerTest extends WicketTestCase
+{
+    @Test
+    void updateFormEnctype()
+    {
+        tester.startPage(MultipartFormComponentListenerPage.class);
+        tester.assertRenderedPage(MultipartFormComponentListenerPage.class);
+
+        TagTester formTagTester = tester.getTagByWicketId("form");
+        assertEquals(Form.ENCTYPE_MULTIPART_FORM_DATA, 
formTagTester.getAttribute("enctype"));
+
+        tester.getRequest().setAttribute("form:dropDown", 1);
+        tester.executeAjaxEvent("form:dropDown", "change");
+        String ajaxResponse = tester.getLastResponseAsString();
+        assertTrue(ajaxResponse.contains(".form.enctype='" + 
MultipartFormComponentListener.ENCTYPE_URL_ENCODED + "'})();"));
+
+        tester.getRequest().setAttribute("form:dropDown", 2);
+        tester.executeAjaxEvent("form:dropDown", "change");
+        ajaxResponse = tester.getLastResponseAsString();
+        assertTrue(ajaxResponse.contains(".form.enctype='" + 
Form.ENCTYPE_MULTIPART_FORM_DATA + "'})();"));
+    }
+}

Reply via email to