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

solomax pushed a commit to branch wicket-8.x
in repository https://gitbox.apache.org/repos/asf/wicket.git


The following commit(s) were added to refs/heads/wicket-8.x by this push:
     new bc7fd4d646 [WICKET-7035] fileCountMax is added
bc7fd4d646 is described below

commit bc7fd4d6464dfa984ecd8b34a32d4f632fbe4625
Author: Maxim Solodovnik <[email protected]>
AuthorDate: Sat Mar 25 10:33:40 2023 +0700

    [WICKET-7035] fileCountMax is added
---
 pom.xml                                            |   2 +-
 .../java/org/apache/wicket/Application.properties  |   5 +-
 .../apache/wicket/Application_ru.properties.xml    |   8 +-
 .../org/apache/wicket/markup/html/form/Form.java   | 207 ++++++++++++---------
 .../http/servlet/MultipartServletWebRequest.java   |  22 ++-
 .../servlet/MultipartServletWebRequestImpl.java    |  24 +--
 .../wicket/examples/upload/MultiUploadPage.java    |  15 +-
 .../apache/wicket/examples/upload/UploadPage.html  |   1 +
 .../apache/wicket/examples/upload/UploadPage.java  |  13 +-
 .../org/apache/wicket/examples/StartExamples.java  |   2 +-
 .../extensions/ajax/AjaxFileDropBehavior.java      |  39 +++-
 11 files changed, 214 insertions(+), 124 deletions(-)

diff --git a/pom.xml b/pom.xml
index cbb86d0b71..e8f9beea2b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -126,7 +126,7 @@
                <cglib.version>3.2.12</cglib.version>
                <commons-collections.version>3.2.2</commons-collections.version>
                <commons-collections4.version>4.4</commons-collections4.version>
-               <commons-fileupload.version>1.4</commons-fileupload.version>
+               <commons-fileupload.version>1.5</commons-fileupload.version>
                <commons-lang3.version>3.9</commons-lang3.version>
                <jacoco.version>0.8.2</jacoco.version>
                <jackson.version>2.9.9</jackson.version>
diff --git a/wicket-core/src/main/java/org/apache/wicket/Application.properties 
b/wicket-core/src/main/java/org/apache/wicket/Application.properties
index ca23a009af..ca12fef6cd 100644
--- a/wicket-core/src/main/java/org/apache/wicket/Application.properties
+++ b/wicket-core/src/main/java/org/apache/wicket/Application.properties
@@ -25,7 +25,7 @@ MinimumValidator=The value of '${label}' must be at least 
${minimum}.
 #deprecated, use RangeValidator.maximum
 MaximumValidator=The value of '${label}' must be at most ${maximum}.
 
-#raplaced with RangeValidator 
+#raplaced with RangeValidator
 NumberValidator.positive=The value of '${label}' must be a positive number.
 #replaced with RangeValidator
 NumberValidator.negative=The value of '${label}' must be a negative number.
@@ -57,11 +57,12 @@ PagingNavigator.last=Go to last page
 PagingNavigation.page=Go to page ${page}
 
 org.apache.wicket.mfu.caption.unlimited=Files:
-org.apache.wicket.mfu.caption.limited=Files (maximum ${max}): 
+org.apache.wicket.mfu.caption.limited=Files (maximum ${max}):
 org.apache.wicket.mfu.delete=Delete
 
 uploadTooLarge = File must be less than ${maxSize}.
 uploadSingleFileTooLarge = Each of files must be less than ${fileMaxSize}.
+uploadTooManyFiles = There should be no more than ${fileCountMax} files.
 uploadFailed = File failed to upload: ${exception.localizedMessage}
 
 FeedbackMessage.CSS.undefined=feedbackPanelUNDEFINED
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/Application_ru.properties.xml 
b/wicket-core/src/main/java/org/apache/wicket/Application_ru.properties.xml
index f3f9b4a823..8f2e414b15 100644
--- a/wicket-core/src/main/java/org/apache/wicket/Application_ru.properties.xml
+++ b/wicket-core/src/main/java/org/apache/wicket/Application_ru.properties.xml
@@ -48,16 +48,18 @@
 
        <entry key="null">Выберите значение</entry>
        <entry key="nullValid"></entry>
-       
+
        <entry key="PagingNavigator.first">Перейти на первую страницу</entry>
        <entry key="PagingNavigator.previous">Перейти на предыдущую 
страницу</entry>
        <entry key="PagingNavigator.next">Перейти на следующую страницу</entry>
        <entry key="PagingNavigator.last">Перейти на последнюю страницу</entry>
        <entry key="PagingNavigation.page">Перейти на страницу ${page}</entry>
-       
+
        <entry key="org.apache.wicket.mfu.caption.unlimited">Файлы:</entry>
        <entry key="org.apache.wicket.mfu.caption.limited">Файлы (всего не 
больше ${max}): </entry>
-       
+
        <entry key="uploadTooLarge">Размер файла не должен превышать 
${maxSize}.</entry>
+       <entry key="uploadTooManyFiles">Количество загружаемых файлов не должно 
превышать ${fileCountMax}.</entry>
+
        <entry key="uploadFailed">Ошибка загрузки файла на сервер: 
${exception.localizedMessage}.</entry>
 </properties>
\ No newline at end of file
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Form.java 
b/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Form.java
index 1416697e37..fc64c33364 100644
--- a/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Form.java
+++ b/wicket-core/src/main/java/org/apache/wicket/markup/html/form/Form.java
@@ -26,6 +26,7 @@ import java.util.Map;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.fileupload.FileCountLimitExceededException;
 import org.apache.commons.fileupload.FileUploadBase;
 import org.apache.commons.fileupload.FileUploadException;
 import org.apache.wicket.Component;
@@ -106,7 +107,7 @@ import org.slf4j.LoggerFactory;
  * </ul>
  * </li>
  * </ul>
- * 
+ *
  * A Form can be configured for handling uploads with multipart requests (e.g. 
files) by calling
  * {@link #setMultiPart(boolean)} (although Wicket will try to automatically 
detect this for you).
  * Use this with {@link FileUploadField} components. You can attach multiple 
{@link FileUploadField}
@@ -115,11 +116,11 @@ import org.slf4j.LoggerFactory;
  * In case of an upload error two resource keys are available to specify error 
messages:
  * {@code uploadTooLarge} and {@code uploadFailed}, i.e. for a form with id 
{@code myform} in
  * {@code MyPage.properties}:
- * 
+ *
  * <pre>
  * myform.uploadTooLarge=You have uploaded a file that is over the allowed 
limit of 2Mb
  * </pre>
- * 
+ *
  * Forms can be nested. You can put a form in another form. Since HTML doesn't 
allow nested
  * &lt;form&gt; tags, the inner forms will be rendered using the &lt;div&gt; 
tag. You have to submit
  * the inner forms using explicit components (like {@link Button} or {@link 
SubmitLink}), you can't
@@ -128,7 +129,7 @@ import org.slf4j.LoggerFactory;
  * <p>
  * When a nested form is submitted, the user entered values in outer (parent) 
forms are preserved
  * and only the fields in the submitted form are validated. </b>
- * 
+ *
  * @author Jonathan Locke
  * @author Juergen Donnerstag
  * @author Eelco Hillenius
@@ -136,7 +137,7 @@ import org.slf4j.LoggerFactory;
  * @author Johan Compagner
  * @author Igor Vaynberg (ivaynberg)
  * @author David Leangen
- * 
+ *
  * @param <T>
  *            The model object type
  */
@@ -149,7 +150,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Visitor used for validation
-        * 
+        *
         * @author Igor Vaynberg (ivaynberg)
         */
        public abstract static class ValidationVisitor implements 
IVisitor<FormComponent<?>, Void>
@@ -178,7 +179,7 @@ public class Form<T> extends WebMarkupContainer
 
                /**
                 * Callback that should be used to validate form component
-                * 
+                *
                 * @param formComponent
                 */
                public abstract void validate(FormComponent<?> formComponent);
@@ -187,7 +188,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Visitor used to update component models
-        * 
+        *
         * @author Igor Vaynberg (ivaynberg)
         */
        private static class FormModelUpdateVisitor implements 
IVisitor<Component, Void>
@@ -196,7 +197,7 @@ public class Form<T> extends WebMarkupContainer
 
                /**
                 * Constructor
-                * 
+                *
                 * @param formFilter
                 */
                public FormModelUpdateVisitor(Form<?> formFilter)
@@ -251,6 +252,7 @@ public class Form<T> extends WebMarkupContainer
 
        private static final String UPLOAD_TOO_LARGE_RESOURCE_KEY = 
"uploadTooLarge";
        private static final String UPLOAD_SINGLE_FILE_TOO_LARGE_RESOURCE_KEY = 
"uploadSingleFileTooLarge";
+       private static final String UPLOAD_TOO_MANY_FILES_RESOURCE_KEY = 
"uploadTooManyFiles";
 
        /**
         * Any default IFormSubmittingComponent. If set, a hidden submit 
component will be rendered
@@ -276,6 +278,12 @@ public class Form<T> extends WebMarkupContainer
         */
        private Bytes fileMaxSize;
 
+       /**
+        * Maximum amount of files in request.
+        * A value of -1 indicates no maximum.
+        */
+       private long fileCountMax = -1L;
+
        /** True if the form has enctype of multipart/form-data */
        private short multiPart = 0;
 
@@ -299,7 +307,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Constructs a form with no validation.
-        * 
+        *
         * @param id
         *            See Component
         */
@@ -323,7 +331,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Adds a form validator to the form.
-        * 
+        *
         * @param validator
         *            validator
         * @throws IllegalArgumentException
@@ -346,7 +354,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Removes a form validator from the form.
-        * 
+        *
         * @param validator
         *            validator
         * @throws IllegalArgumentException
@@ -411,7 +419,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Registers an error feedback message for this component
-        * 
+        *
         * @param error
         *            error message
         * @param args
@@ -426,7 +434,7 @@ public class Form<T> extends WebMarkupContainer
         * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
         * <p>
         * Gets the IFormSubmittingComponent which submitted this form.
-        * 
+        *
         * @return The component which submitted this form, or null if the 
processing was not triggered
         *         by a registered IFormSubmittingComponent
         */
@@ -471,7 +479,7 @@ public class Form<T> extends WebMarkupContainer
         * </p>
         * There can be only one default submit component per form hierarchy. 
So if you want to get the
         * default component on a nested form, it will actually delegate the 
call to root form. </b>
-        * 
+        *
         * @return The submit component to set as the default 
IFormSubmittingComponent, or null when you
         *         want to 'unset' any previously set default 
IFormSubmittingComponent
         */
@@ -489,7 +497,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Gets all {@link IFormValidator}s added to this form
-        * 
+        *
         * @return unmodifiable collection of {@link IFormValidator}s
         */
        public final Collection<IFormValidator> getFormValidators()
@@ -516,10 +524,10 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Generate a piece of JavaScript that submits the form to the given 
URL of an {@link IRequestListener}.
-        * 
+        *
         * Warning: This code should only be called in the rendering phase for 
form components inside
         * the form because it uses the css/javascript id of the form which can 
be stored in the markup.
-        * 
+        *
         * @param url
         *            The listener url to be submitted to
         * @return the javascript code that submits the form.
@@ -529,20 +537,20 @@ public class Form<T> extends WebMarkupContainer
                Form<?> root = getRootForm();
 
                AppendingStringBuffer buffer = new AppendingStringBuffer();
-               
+
                String action = url.toString();
                if (root.encodeUrlInHiddenFields()) {
                        
buffer.append(String.format("document.getElementById('%s').innerHTML = '", 
root.getHiddenFieldsId()));
-                       
+
                        // parameter must be sent as hidden field, as it would 
be ignored in the action URL
                        int i = action.indexOf('?');
                        if (i != -1)
                        {
                                
writeParamsAsHiddenFields(Strings.split(action.substring(i + 1), '&'), buffer);
-                               
+
                                action = action.substring(0, i);
                        }
-                       
+
                        buffer.append("';");
                }
                buffer.append(String.format("var f = 
document.getElementById('%s');", root.getMarkupId()));
@@ -553,11 +561,11 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Generate a piece of JavaScript that submits the form with the given 
{@link IFormSubmittingComponent}.
-        * 
+        *
         * @param submitter
         *            the submitter
         * @return the javascript code that submits the form.
-        * 
+        *
         * @see #findSubmittingButton()
         */
        public final CharSequence getJsForSubmitter(IFormSubmittingComponent 
submitter)
@@ -571,9 +579,9 @@ public class Form<T> extends WebMarkupContainer
                if (root.encodeUrlInHiddenFields())
                {
                        
buffer.append(String.format("document.getElementById('%s').innerHTML += '", 
root.getHiddenFieldsId()));
-                       
+
                        writeParamsAsHiddenFields(new String[] {param}, buffer);
-                       
+
                        buffer.append("';");
                }
                else
@@ -588,8 +596,8 @@ public class Form<T> extends WebMarkupContainer
        /**
         * Gets the maximum size for uploads. If null, the setting
         * {@link 
org.apache.wicket.settings.ApplicationSettings#getDefaultMaximumUploadSize()} 
is used.
-        * 
-        * 
+        *
+        *
         * @return the maximum size
         */
        public final Bytes getMaxSize()
@@ -599,7 +607,7 @@ public class Form<T> extends WebMarkupContainer
                 * max size smaller then the one specified in applications 
settings because the inner form
                 * will return the default unless it is specifically set in the 
traversal. With this method
                 * remaining final we can tell when the value is explicitly set 
by the user.
-                * 
+                *
                 * If the value needs to be dynamic it can be set in 
oncofigure() instead of overriding this
                 * method.
                 */
@@ -625,7 +633,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Gets maximum size for each file of an upload.
-        * 
+        *
         * @return
         */
        public Bytes getFileMaxSize()
@@ -633,9 +641,19 @@ public class Form<T> extends WebMarkupContainer
                return fileMaxSize;
        }
 
+       /**
+        * Gets maximum count of files in the form
+        *
+        * @return
+        */
+       public long getFileCountMax()
+       {
+               return fileCountMax;
+       }
+
        /**
         * Returns the root form or this, if this is the root form.
-        * 
+        *
         * @return root form or this form
         */
        public Form<?> getRootForm()
@@ -666,7 +684,7 @@ public class Form<T> extends WebMarkupContainer
         * <p>
         * Returned prefix will be used for all form components. The prefix can 
also be overridden on
         * form component level by overriding {@link 
FormComponent#getValidatorKeyPrefix()}
-        * 
+        *
         * @return prefix prepended to validator keys
         */
        public String getValidatorKeyPrefix()
@@ -676,7 +694,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Gets whether the current form has any error registered.
-        * 
+        *
         * @return True if this form has at least one error.
         */
        public final boolean hasError()
@@ -695,7 +713,7 @@ public class Form<T> extends WebMarkupContainer
        /**
         * Returns whether the form is a root form, which means that there's no 
other form in it's
         * parent hierarchy.
-        * 
+        *
         * @return true if form is a root form, false otherwise
         */
        public boolean isRootForm()
@@ -705,7 +723,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Checks if this form has been submitted during the current request
-        * 
+        *
         * @return true if the form has been submitted during this request, 
false otherwise
         */
        public final boolean isSubmitted()
@@ -715,9 +733,9 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * THIS METHOD IS NOT PART OF THE WICKET API. DO NOT ATTEMPT TO 
OVERRIDE OR CALL IT.
-        * 
+        *
         * Handles form submissions.
-        * 
+        *
         * @see #onFormSubmitted(IFormSubmitter)
         */
        @Override
@@ -731,7 +749,7 @@ public class Form<T> extends WebMarkupContainer
         * {@link #getMethod()}. For example, someone can copy and paste the 
action url and invoke the
         * form using a {@code GET} instead of the desired {@code POST}. This 
method allows the user to
         * react to this situation.
-        * 
+        *
         * @return response that can either abort or continue the processing of 
the form
         */
        protected MethodMismatchResponse onMethodMismatch()
@@ -741,13 +759,13 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * THIS METHOD IS NOT PART OF THE WICKET API. DO NOT ATTEMPT TO 
OVERRIDE OR CALL IT.
-        * 
+        *
         * Handles form submissions.
-        * 
+        *
         * @param submitter
         *            listener that will receive form processing events, if 
{@code null} the form will
         *            attempt to locate one
-        * 
+        *
         * @see Form#validate()
         */
        public final void onFormSubmitted(IFormSubmitter submitter)
@@ -866,7 +884,7 @@ public class Form<T> extends WebMarkupContainer
         * </ul>
         * </li>
         * </ul>
-        * 
+        *
         * @param submitter
         *            The submitting component, if any. May be null.
         * @return The form that needs to be processed.
@@ -919,7 +937,7 @@ public class Form<T> extends WebMarkupContainer
         * Whether this form wants to be submitted too if a nested form is 
submitted. By default, this
         * is false, so when a nested form is submitted, this form will 
<em>not</em> be submitted. If
         * this method is overridden to return true, this form <em>will</em> be 
submitted.
-        * 
+        *
         * @return Whether this form wants to be submitted too if a nested form 
is submitted.
         */
        // TODO wicket-7 migration guide: changed from public to protected
@@ -932,7 +950,7 @@ public class Form<T> extends WebMarkupContainer
         * Whether this *nested* form wants to be submitted when parent form is 
submitted. By default,
         * this is true, so when a parent form is submitted, the nested form is 
also submitted. If this
         * method is overridden to return false, it will not be validated, 
processed nor submitted.
-        * 
+        *
         * @return {@code true} by default
         */
        protected boolean wantSubmitOnParentFormSubmit()
@@ -943,15 +961,15 @@ public class Form<T> extends WebMarkupContainer
        /**
         * Process the form. Though you can override this method to provide 
your own algorithm, it is
         * not recommended to do so.
-        * 
+        *
         * <p>
         * See the class documentation for further details on the form 
processing
         * </p>
-        * 
+        *
         * @param submittingComponent
         *            component responsible for submitting the form, or 
<code>null</code> if none (eg
         *            the form has been submitted via the enter key or 
javascript calling form.submit())
-        * 
+        *
         * @see #delegateSubmit(IFormSubmitter) for an easy way to process 
submitting component in the
         *      default manner
         */
@@ -1005,7 +1023,7 @@ public class Form<T> extends WebMarkupContainer
        /**
         * Calls onError on this {@link Form} and any enabled and visible 
nested form, if the respective
         * {@link Form} actually has errors.
-        * 
+        *
         * @param submitter
         */
        protected void callOnError(IFormSubmitter submitter)
@@ -1039,20 +1057,20 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Sets FLAG_SUBMITTED to true on this form and every enabled nested 
form.
-        * @param submitter 
+        * @param submitter
         */
        private void markFormsSubmitted(IFormSubmitter submitter)
        {
                setFlag(FLAG_SUBMITTED, true);
                Form<?> formToProcess = findFormToProcess(submitter);
-               
+
                visitChildren(Form.class, new IVisitor<Component, Void>()
                {
                        @Override
                        public void component(final Component component, final 
IVisit<Void> visit)
                        {
                                Form<?> form = (Form<?>)component;
-                               if ((form.wantSubmitOnParentFormSubmit() || 
form == formToProcess) 
+                               if ((form.wantSubmitOnParentFormSubmit() || 
form == formToProcess)
                                        && form.isEnabledInHierarchy() && 
form.isVisibleInHierarchy())
                                {
                                        form.setFlag(FLAG_SUBMITTED, true);
@@ -1074,7 +1092,7 @@ public class Form<T> extends WebMarkupContainer
         * </p>
         * There can be only one default button per form hierarchy. So if you 
set default button on a
         * nested form, it will actually delegate the call to root form. </b>
-        * 
+        *
         * @param submittingComponent
         *            The component to set as the default submitting component, 
or null when you want to
         *            'unset' any previously set default component
@@ -1094,7 +1112,7 @@ public class Form<T> extends WebMarkupContainer
        /**
         * Sets the maximum size for uploads. If null, the setting
         * {@link 
org.apache.wicket.settings.ApplicationSettings#getDefaultMaximumUploadSize()} 
is used.
-        * 
+        *
         * @param maxSize
         *            The maximum size
         */
@@ -1105,7 +1123,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Sets maximum size of each file in upload request.
-        * 
+        *
         * @param fileMaxSize
         */
        public void setFileMaxSize(Bytes fileMaxSize)
@@ -1113,10 +1131,20 @@ public class Form<T> extends WebMarkupContainer
                this.fileMaxSize = fileMaxSize;
        }
 
+       /**
+        * Sets maximum amount of files in upload request.
+        *
+        * @param fileCountMax
+        */
+       public void setFileCountMax(long fileCountMax)
+       {
+               this.fileCountMax = fileCountMax;
+       }
+
        /**
         * Set to true to use enctype='multipart/form-data', and to process 
file uploads by default
         * multiPart = false
-        * 
+        *
         * @param multiPart
         *            whether this form should behave as a multipart form
         */
@@ -1154,7 +1182,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Convenient and typesafe way to visit all the form components on a 
form.
-        * 
+        *
         * @param <R>
         *            return object type
         * @param visitor
@@ -1169,7 +1197,7 @@ public class Form<T> extends WebMarkupContainer
        /**
         * Convenient and typesafe way to visit all the form components on a 
form postorder (deepest
         * first)
-        * 
+        *
         * @param <R>
         *            Return object type
         * @param visitor
@@ -1184,7 +1212,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Find out whether there is any registered error for a form component.
-        * 
+        *
         * @return whether there is any registered error for a form component
         */
        private boolean anyFormComponentError()
@@ -1251,7 +1279,7 @@ public class Form<T> extends WebMarkupContainer
 
                // close div
                buffer.append("</div>");
-               
+
                getResponse().write(buffer);
        }
 
@@ -1277,7 +1305,7 @@ public class Form<T> extends WebMarkupContainer
         * Regardless of whether a submitting component was found, the form's 
onSubmit method is called
         * next.
         * </p>
-        * 
+        *
         * @param submittingComponent
         *            the component that triggered this form processing, or 
null if the processing was
         *            triggered by something else (like a non-Wicket submit 
button or a javascript
@@ -1322,7 +1350,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Returns the id which will be used for the hidden div containing all 
parameter fields.
-        * 
+        *
         * @return the id of the hidden div
         */
        private final String getHiddenFieldsId()
@@ -1337,7 +1365,7 @@ public class Form<T> extends WebMarkupContainer
         * handlers may submit the form with a "get" even when the form method 
is declared as "post."
         * Therefore this method should not be considered a guarantee of the 
HTTP method used, but a
         * value for the markup only. Override if you have a requirement to 
alter this behavior.
-        * 
+        *
         * @return the submit method specified in markup.
         */
        protected String getMethod()
@@ -1347,7 +1375,7 @@ public class Form<T> extends WebMarkupContainer
        }
 
        /**
-        * 
+        *
         * @see org.apache.wicket.Component#getStatelessHint()
         */
        @Override
@@ -1408,7 +1436,7 @@ public class Form<T> extends WebMarkupContainer
        /**
         * Handles multi-part processing of the submitted data. <h3>
         * WARNING</h3> If this method is overridden it can break {@link 
FileUploadField}s on this form
-        * 
+        *
         * @return false if form is multipart and upload failed
         */
        protected boolean handleMultiPart()
@@ -1423,6 +1451,7 @@ public class Form<T> extends WebMarkupContainer
                                final MultipartServletWebRequest 
multipartWebRequest = request.newMultipartWebRequest(
                                        getMaxSize(), getPage().getId());
                                
multipartWebRequest.setFileMaxSize(getFileMaxSize());
+                               
multipartWebRequest.setFileCountMax(getFileCountMax());
                                multipartWebRequest.parseFileParts();
 
                                // TODO: Can't this be detected from header?
@@ -1435,6 +1464,7 @@ public class Form<T> extends WebMarkupContainer
                                model.put("exception", fux);
                                model.put("maxSize", getMaxSize());
                                model.put("fileMaxSize", getFileMaxSize());
+                               model.put("fileCountMax", getFileCountMax());
 
                                onFileUploadException(fux, model);
 
@@ -1451,7 +1481,7 @@ public class Form<T> extends WebMarkupContainer
         * maxSize in the model or add you own property and use that in your 
error message.
         * <p>
         * Don't forget to call super.onFileUploadException(e, model) at the 
end of your method.
-        * 
+        *
         * @param e
         * @param model
         */
@@ -1468,6 +1498,11 @@ public class Form<T> extends WebMarkupContainer
                        String msg = 
getString(UPLOAD_SINGLE_FILE_TOO_LARGE_RESOURCE_KEY, Model.ofMap(model));
                        error(msg);
                }
+               else if (e instanceof FileCountLimitExceededException)
+               {
+                       String msg = 
getString(UPLOAD_TOO_MANY_FILES_RESOURCE_KEY, Model.ofMap(model));
+                       error(msg);
+               }
                else
                {
                        String msg = getString(UPLOAD_FAILED_RESOURCE_KEY, 
Model.ofMap(model));
@@ -1637,7 +1672,7 @@ public class Form<T> extends WebMarkupContainer
                        tag.remove("enctype");
                }
        }
-       
+
        // WICKET-6658 form is not allowed, anything else can stay as is
        private void adjustNestedTagName(ComponentTag tag) {
                if ("form".equalsIgnoreCase(tag.getName()))
@@ -1648,7 +1683,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Generates the action url for the form
-        * 
+        *
         * @return action url
         */
        protected CharSequence getActionUrl()
@@ -1684,7 +1719,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Append an additional hidden input tag to support anchor tags that 
can submit a form.
-        * 
+        *
         * @param markupStream
         *            The markup stream
         * @param openTag
@@ -1717,7 +1752,7 @@ public class Form<T> extends WebMarkupContainer
 
                        getResponse().write(String.format("<div id=\"%s\" 
style=\"width:0px;height:0px;position:absolute;left:-100px;top:-100px;overflow:hidden\"
 class=\"%s\">", getHiddenFieldsId(), cssClass));
 
-                       AppendingStringBuffer buffer = new 
AppendingStringBuffer();                             
+                       AppendingStringBuffer buffer = new 
AppendingStringBuffer();
 
                        String url = getActionUrl().toString();
                        int i = url.indexOf('?');
@@ -1727,7 +1762,7 @@ public class Form<T> extends WebMarkupContainer
                        writeParamsAsHiddenFields(params, buffer);
 
                        getResponse().write(buffer);
-                       
+
                        getResponse().write("</div>");
                }
 
@@ -1744,7 +1779,7 @@ public class Form<T> extends WebMarkupContainer
        }
 
        /**
-        * 
+        *
         * @param params
         * @param buffer
         */
@@ -1764,7 +1799,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Take URL-encoded query string value, decode it and return 
HTML-escaped version
-        * 
+        *
         * @param s
         *            value to decode
         * @return URL decoded and HTML escaped value
@@ -1822,10 +1857,10 @@ public class Form<T> extends WebMarkupContainer
         * Update the model of all components on this form and nested forms 
using the fields that were
         * sent with the current request. This method only updates models when 
the Form.validate() is
         * called first that takes care of the conversion for the 
FormComponents.
-        * 
+        *
         * Normally this method will not be called when a validation error 
occurs in one of the form
         * components.
-        * 
+        *
         * @see org.apache.wicket.markup.html.form.FormComponent#updateModel()
         */
        protected final void updateFormComponentModels()
@@ -1836,7 +1871,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Update the model of all components on nested forms.
-        * 
+        *
         * @see #updateFormComponentModels()
         */
        private void updateNestedFormComponentModels()
@@ -1860,7 +1895,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Update the model of all components on this form.
-        * 
+        *
         * @see #updateFormComponentModels()
         */
        private void internalUpdateFormComponentModels()
@@ -1924,7 +1959,7 @@ public class Form<T> extends WebMarkupContainer
        /**
         * Called after form components have updated their models. This is a 
late-stage validation that
         * allows outside frameworks to validate any beans that the form is 
updating.
-        * 
+        *
         * This validation method is not preferred because at this point any 
errors will not unroll any
         * changes to the model object, so the model object is in a modified 
state potentially
         * containing illegal values. However, with external frameworks there 
may not be an alternate
@@ -1957,10 +1992,10 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Checks if the specified form component visible and is attached to a 
page
-        * 
+        *
         * @param fc
         *            form component
-        * 
+        *
         * @return true if the form component and all its parents are visible 
and there component is in
         *         page's hierarchy
         */
@@ -1976,7 +2011,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Validates form with the given form validator
-        * 
+        *
         * @param validator
         */
        protected final void validateFormValidator(final IFormValidator 
validator)
@@ -2036,7 +2071,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Validates {@link FormComponent}s as well as {@link IFormValidator}s 
in nested {@link Form}s.
-        * 
+        *
         * @see #validate()
         */
        private void validateNestedForms()
@@ -2065,7 +2100,7 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Allows to customize input names of form components inside this form.
-        * 
+        *
         * @return String that well be used as prefix to form component input 
names
         */
        protected String getInputNamePrefix()
@@ -2085,7 +2120,7 @@ public class Form<T> extends WebMarkupContainer
        /**
         * Utility method to assemble an id to distinct form components from 
different nesting levels.
         * Useful to generate input names attributes.
-        * 
+        *
         * @param component
         * @return form relative identification string
         */
@@ -2111,7 +2146,7 @@ public class Form<T> extends WebMarkupContainer
                 * Certain input names causes problems with JavaScript. If the 
input name would cause a
                 * problem, we create a replacement unique name by prefixing 
the name with a path that would
                 * otherwise never be used (blank id in path).
-                * 
+                *
                 * Input names must start with [A-Za-z] according to HTML 4.01 
spec. HTML 5 allows almost
                 * anything.
                 */
@@ -2126,9 +2161,9 @@ public class Form<T> extends WebMarkupContainer
 
        /**
         * Response when a submission method mismatch is detected
-        * 
+        *
         * @see Form#getMethod()
-        * 
+        *
         * @author igor
         */
        public static enum MethodMismatchResponse {
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/MultipartServletWebRequest.java
 
b/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/MultipartServletWebRequest.java
index 077cff441f..45fcd79523 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/MultipartServletWebRequest.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/MultipartServletWebRequest.java
@@ -31,7 +31,7 @@ import org.apache.wicket.util.lang.Bytes;
 
 /**
  * Servlet specific WebRequest subclass for multipart content uploads.
- * 
+ *
  * @author Matej Knopp
  */
 public abstract class MultipartServletWebRequest extends ServletWebRequest
@@ -48,9 +48,15 @@ public abstract class MultipartServletWebRequest extends 
ServletWebRequest
         */
        private Bytes fileMaxSize;
 
+       /**
+        * Maximum amount of files in request.
+        * A value of -1 indicates no maximum.
+        */
+       private long fileCountMax = -1L;
+
        /**
         * Construct.
-        * 
+        *
         * @param httpServletRequest
         * @param filterPrefix
         */
@@ -61,7 +67,7 @@ public abstract class MultipartServletWebRequest extends 
ServletWebRequest
 
        /**
         * Construct.
-        * 
+        *
         * @param httpServletRequest
         * @param filterPrefix
         * @param url
@@ -131,4 +137,14 @@ public abstract class MultipartServletWebRequest extends 
ServletWebRequest
        {
                this.fileMaxSize = fileMaxSize;
        }
+
+       public long getFileCountMax()
+       {
+               return fileCountMax;
+       }
+
+       public void setFileCountMax(long fileCountMax)
+       {
+               this.fileCountMax = fileCountMax;
+       }
 }
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/MultipartServletWebRequestImpl.java
 
b/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/MultipartServletWebRequestImpl.java
index d0d5330b70..355f8442a9 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/MultipartServletWebRequestImpl.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/MultipartServletWebRequestImpl.java
@@ -48,7 +48,7 @@ import org.apache.wicket.util.value.ValueMap;
 
 /**
  * Servlet specific WebRequest subclass for multipart content uploads.
- * 
+ *
  * @author Jonathan Locke
  * @author Eelco Hillenius
  * @author Cameron Braid
@@ -298,12 +298,14 @@ public class MultipartServletWebRequestImpl extends 
MultipartServletWebRequest
                        fileUpload.setFileSizeMax(fileMaxSize.bytes());
                }
 
+               fileUpload.setFileCountMax(getFileCountMax());
+
                return fileUpload;
        }
 
     /**
         * Adds a parameter to the parameters value map
-        * 
+        *
         * @param name
         *            parameter name
         * @param value
@@ -341,7 +343,7 @@ public class MultipartServletWebRequestImpl extends 
MultipartServletWebRequest
 
        /**
         * Gets the file that was uploaded using the given field name.
-        * 
+        *
         * @param fieldName
         *            the field name that was used for the upload
         * @return the upload with the given field name
@@ -376,7 +378,7 @@ public class MultipartServletWebRequestImpl extends 
MultipartServletWebRequest
        /**
         * Subclasses that want to receive upload notifications should return 
true. By default it takes
         * the value from {@link 
org.apache.wicket.settings.ApplicationSettings#isUploadProgressUpdatesEnabled()}.
-        * 
+        *
         * @return true if upload status update event should be invoked
         */
        protected boolean wantUploadProgressUpdates()
@@ -386,7 +388,7 @@ public class MultipartServletWebRequestImpl extends 
MultipartServletWebRequest
 
        /**
         * Upload start callback
-        * 
+        *
         * @param totalBytes
         */
        protected void onUploadStarted(int totalBytes)
@@ -398,7 +400,7 @@ public class MultipartServletWebRequestImpl extends 
MultipartServletWebRequest
 
        /**
         * Upload status update callback
-        * 
+        *
         * @param bytesUploaded
         * @param total
         */
@@ -426,7 +428,7 @@ public class MultipartServletWebRequestImpl extends 
MultipartServletWebRequest
 
        /**
         * An {@link InputStream} that updates total number of bytes read
-        * 
+        *
         * @author Igor Vaynberg (ivaynberg)
         */
        private class CountingInputStream extends InputStream
@@ -436,7 +438,7 @@ public class MultipartServletWebRequestImpl extends 
MultipartServletWebRequest
 
                /**
                 * Constructs a new CountingInputStream.
-                * 
+                *
                 * @param in
                 *            InputStream to delegate to
                 */
@@ -527,7 +529,7 @@ public class MultipartServletWebRequestImpl extends 
MultipartServletWebRequest
 
        /**
         * Retrieves {@link UploadInfo} from session, null if not found.
-        * 
+        *
         * @param req
         *            http servlet request, not null
         * @param upload
@@ -542,7 +544,7 @@ public class MultipartServletWebRequestImpl extends 
MultipartServletWebRequest
 
        /**
         * Sets the {@link UploadInfo} object into session.
-        * 
+        *
         * @param req
         *            http servlet request, not null
         * @param upload
@@ -561,7 +563,7 @@ public class MultipartServletWebRequestImpl extends 
MultipartServletWebRequest
 
        /**
         * Clears the {@link UploadInfo} object from session if one exists.
-        * 
+        *
         * @param req
         *            http servlet request, not null
         * @param upload
diff --git 
a/wicket-examples/src/main/java/org/apache/wicket/examples/upload/MultiUploadPage.java
 
b/wicket-examples/src/main/java/org/apache/wicket/examples/upload/MultiUploadPage.java
index 889e941179..b44ae8ef62 100644
--- 
a/wicket-examples/src/main/java/org/apache/wicket/examples/upload/MultiUploadPage.java
+++ 
b/wicket-examples/src/main/java/org/apache/wicket/examples/upload/MultiUploadPage.java
@@ -43,7 +43,7 @@ import org.apache.wicket.util.lang.Bytes;
 
 /**
  * Upload example.
- * 
+ *
  * @author Eelco Hillenius
  */
 public class MultiUploadPage extends WicketExamplePage
@@ -55,7 +55,7 @@ public class MultiUploadPage extends WicketExamplePage
        {
                /**
                 * Construct.
-                * 
+                *
                 * @param name
                 *            Component name
                 * @param files
@@ -95,7 +95,7 @@ public class MultiUploadPage extends WicketExamplePage
 
                /**
                 * TODO
-                * 
+                *
                 * @return Collection
                 */
                public Collection<FileUpload> getUploads()
@@ -105,7 +105,7 @@ public class MultiUploadPage extends WicketExamplePage
 
                /**
                 * Construct.
-                * 
+                *
                 * @param id
                 *            Component id
                 */
@@ -125,6 +125,9 @@ public class MultiUploadPage extends WicketExamplePage
 
                        // Set maximum size per file to 90K for demo purposes
                        setFileMaxSize(Bytes.kilobytes(90));
+
+                       // Set maximum file count to 5 for demo purposes
+                       setFileCountMax(5L);
                }
 
                /**
@@ -161,7 +164,7 @@ public class MultiUploadPage extends WicketExamplePage
 
        /**
         * Constructor.
-        * 
+        *
         * @param parameters
         *            Page parameters
         */
@@ -196,7 +199,7 @@ public class MultiUploadPage extends WicketExamplePage
 
        /**
         * Check whether the file allready exists, and if so, try to delete it.
-        * 
+        *
         * @param newFile
         *            the file to check
         */
diff --git 
a/wicket-examples/src/main/java/org/apache/wicket/examples/upload/UploadPage.html
 
b/wicket-examples/src/main/java/org/apache/wicket/examples/upload/UploadPage.html
index 5356904adb..d5c2b76017 100644
--- 
a/wicket-examples/src/main/java/org/apache/wicket/examples/upload/UploadPage.html
+++ 
b/wicket-examples/src/main/java/org/apache/wicket/examples/upload/UploadPage.html
@@ -12,6 +12,7 @@
        <p>Wicket can also handle file uploads via AJAX, please see AJAX 
examples section. AJAX Functionality is not included in this example to keep 
things simple.</p>
        <p>The maximum file size of a single upload is: 90k!</p>
        <p>The maximum size of uploading several files (third demo) is: 
100k!</p>
+       <p>The maximum file count of uploading several files (third demo) is: 
5</p>
        <br/>
 
     <form wicket:id="simpleUpload">
diff --git 
a/wicket-examples/src/main/java/org/apache/wicket/examples/upload/UploadPage.java
 
b/wicket-examples/src/main/java/org/apache/wicket/examples/upload/UploadPage.java
index 8ef9177fe9..3fe09b7eef 100644
--- 
a/wicket-examples/src/main/java/org/apache/wicket/examples/upload/UploadPage.java
+++ 
b/wicket-examples/src/main/java/org/apache/wicket/examples/upload/UploadPage.java
@@ -40,7 +40,7 @@ import org.apache.wicket.util.lang.Bytes;
 
 /**
  * Upload example.
- * 
+ *
  * @author Eelco Hillenius
  */
 @SuppressWarnings("serial")
@@ -53,7 +53,7 @@ public class UploadPage extends WicketExamplePage
        {
                /**
                 * Construct.
-                * 
+                *
                 * @param name
                 *            Component name
                 * @param files
@@ -93,7 +93,7 @@ public class UploadPage extends WicketExamplePage
 
                /**
                 * Construct.
-                * 
+                *
                 * @param name
                 *            Component name
                 */
@@ -112,6 +112,9 @@ public class UploadPage extends WicketExamplePage
 
                        // Set maximum size per file to 90K for demo purposes
                        setFileMaxSize(Bytes.kilobytes(90));
+
+                       // Set maximum file count to 5 for demo purposes
+                       setFileCountMax(5L);
                }
 
                @Override
@@ -149,7 +152,7 @@ public class UploadPage extends WicketExamplePage
 
        /**
         * Constructor.
-        * 
+        *
         * @param parameters
         *            Page parameters
         */
@@ -195,7 +198,7 @@ public class UploadPage extends WicketExamplePage
 
        /**
         * Check whether the file allready exists, and if so, try to delete it.
-        * 
+        *
         * @param newFile
         *            the file to check
         */
diff --git 
a/wicket-examples/src/test/java/org/apache/wicket/examples/StartExamples.java 
b/wicket-examples/src/test/java/org/apache/wicket/examples/StartExamples.java
index 18e6c26082..7e7e5dea80 100644
--- 
a/wicket-examples/src/test/java/org/apache/wicket/examples/StartExamples.java
+++ 
b/wicket-examples/src/test/java/org/apache/wicket/examples/StartExamples.java
@@ -42,7 +42,7 @@ public class StartExamples
 {
        /**
         * Main function, starts the jetty server.
-        * 
+        *
         * @param args
         */
        public static void main(String[] args) throws Exception
diff --git 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/AjaxFileDropBehavior.java
 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/AjaxFileDropBehavior.java
index 0501610c74..a668914a32 100644
--- 
a/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/AjaxFileDropBehavior.java
+++ 
b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/AjaxFileDropBehavior.java
@@ -63,6 +63,12 @@ public class AjaxFileDropBehavior extends AjaxEventBehavior
         */
        private Bytes fileMaxSize;
 
+       /**
+        * Maximum amount of files in request.
+        * A value of -1 indicates no maximum.
+        */
+       private long fileCountMax = -1L;
+
        private String parameterName = "f";
 
        /**
@@ -97,11 +103,11 @@ public class AjaxFileDropBehavior extends AjaxEventBehavior
                        public CharSequence getPrecondition(Component component)
                        {
                                String css = 
getComponent().getString(DRAG_OVER_CLASS_KEY);
-                               
+
                                return String.format("jQuery('#' + 
attrs.c).toggleClass('%s', attrs.event.type === 'dragover'); return 
(attrs.event.type === 'drop');", css);
                        }
                });
-               
+
                attributes.getDynamicExtraParameters()
                        .add(String.format(
                                "return 
Wicket.DataTransfer.getFilesAsParamArray(attrs.event.originalEvent, '%s');",
@@ -117,6 +123,7 @@ public class AjaxFileDropBehavior extends AjaxEventBehavior
                        final MultipartServletWebRequest multipartWebRequest = 
request
                                .newMultipartWebRequest(getMaxSize(), 
getComponent().getPage().getId());
                        multipartWebRequest.setFileMaxSize(getFileMaxSize());
+                       multipartWebRequest.setFileCountMax(getFileCountMax());
                        multipartWebRequest.parseFileParts();
 
                        // TODO: Can't this be detected from header?
@@ -155,7 +162,7 @@ public class AjaxFileDropBehavior extends AjaxEventBehavior
 
        /**
         * Set the maximum upload size.
-        * 
+        *
         * @param maxSize maximum size, must not be null
         */
        public void setMaxSize(Bytes maxSize)
@@ -171,7 +178,7 @@ public class AjaxFileDropBehavior extends AjaxEventBehavior
 
        /**
         * Set an optional maximum size per file.
-        * 
+        *
         * @param fileMaxSize maximum size for each uploaded file
         */
        public void setFileMaxSize(Bytes fileMaxSize)
@@ -179,12 +186,32 @@ public class AjaxFileDropBehavior extends 
AjaxEventBehavior
                this.fileMaxSize = fileMaxSize;
        }
 
+       /**
+        * Gets maximum count of files
+        *
+        * @return
+        */
+       public long getFileCountMax()
+       {
+               return fileCountMax;
+       }
+
+       /**
+        * Sets maximum amount of files in upload request.
+        *
+        * @param fileCountMax
+        */
+       public void setFileCountMax(long fileCountMax)
+       {
+               this.fileCountMax = fileCountMax;
+       }
+
        /**
         * Hook method called after a file was uploaded.
         * <p>
         * Note: {@link #onError(AjaxRequestTarget, FileUploadException)} is 
called instead when
         * uploading failed
-        * 
+        *
         * @param target
         *            the current request handler
         * @param files
@@ -197,7 +224,7 @@ public class AjaxFileDropBehavior extends AjaxEventBehavior
        /**
         * Hook method called to handle any error during uploading of the file.
         * <p>
-        * Default implementation re-throws the exception. 
+        * Default implementation re-throws the exception.
         *
         * @param target
         *            the current request handler


Reply via email to