001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.markup.html.form.upload;
018
019import java.util.ArrayList;
020import java.util.List;
021import org.apache.wicket.Component;
022import org.apache.wicket.WicketRuntimeException;
023import org.apache.wicket.ajax.AjaxRequestTarget;
024import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
025import org.apache.wicket.ajax.attributes.IAjaxCallListener;
026import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
027import org.apache.wicket.markup.head.IHeaderResponse;
028import org.apache.wicket.markup.head.JavaScriptHeaderItem;
029import org.apache.wicket.request.IRequestParameters;
030import org.apache.wicket.request.Request;
031import org.apache.wicket.request.cycle.RequestCycle;
032import org.apache.wicket.request.resource.JavaScriptResourceReference;
033import org.apache.wicket.request.resource.ResourceReference;
034import org.apache.wicket.util.lang.Args;
035import org.apache.wicket.util.string.StringValue;
036import org.danekja.java.util.function.serializable.SerializableBiConsumer;
037import com.github.openjson.JSONArray;
038import com.github.openjson.JSONObject;
039
040/**
041 * {@link org.apache.wicket.ajax.form.OnChangeAjaxBehavior} that streams back to server properties
042 * of the selected file(s) (at client side), before uploading it (them).
043 *
044 * @author Ernesto Reinaldo Barreiro (reiern70@gmail.com).
045 */
046public abstract class FilesSelectedBehavior extends OnChangeAjaxBehavior {
047
048    private static final long serialVersionUID = 1L;
049
050    private static final ResourceReference JS = new JavaScriptResourceReference(FilesSelectedBehavior.class, "FilesSelectedBehavior.js");
051
052    @Override
053    protected void onBind() {
054        super.onBind();
055        Component component = getComponent();
056        if (!(component instanceof FileUploadField)) {
057            throw new WicketRuntimeException("Behavior " + getClass().getName()
058                    + " can only be added to an instance of a FileUploadField");
059        }
060    }
061
062    @Override
063    protected void onUpdate(AjaxRequestTarget target) {
064        Request request = RequestCycle.get().getRequest();
065        List<FileDescription> fileDescriptions = new ArrayList<>();
066        IRequestParameters parameters = request.getRequestParameters();
067        // data is streamed as JSON.
068        StringValue fileInfos = parameters.getParameterValue("fileInfos");
069        JSONArray jsonArray = new JSONArray(fileInfos.toString());
070        for (int i = 0; i < jsonArray.length(); i++)
071        {
072            fileDescriptions.add(new FileDescription((JSONObject)jsonArray.get(i)));
073        }
074        onSelected(target, fileDescriptions);
075    }
076
077
078    /**
079     * Called when a file, at client side is selected.
080     *
081     * @param target           The {@link org.apache.wicket.ajax.AjaxRequestTarget}
082     * @param fileDescriptions A list of FileDescription
083     */
084    protected abstract void onSelected(AjaxRequestTarget target, List<FileDescription> fileDescriptions);
085
086    @Override
087    protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
088    {
089        super.updateAjaxAttributes(attributes);
090        attributes.getAjaxCallListeners().add(new IAjaxCallListener()
091        {
092            @Override
093            public CharSequence getPrecondition(Component component)
094            {
095                return "return Wicket.FilesSelected.precondition(this);";
096            }
097        });
098        attributes.getDynamicExtraParameters().add("return Wicket.FilesSelected.collectFilesDetails('" + getComponent().getMarkupId() + "');");
099    }
100
101    @Override
102    public void renderHead(Component component, IHeaderResponse response)
103    {
104        super.renderHead(component, response);
105        response.render(JavaScriptHeaderItem.forReference(JS));
106    }
107
108    /**
109     * Creates an {@link FilesSelectedBehavior} based on lambda expressions
110     *
111     * @param select {@link SerializableBiConsumer}
112     *
113     * @return the {@link FilesSelectedBehavior} behavior
114     */
115    public static FilesSelectedBehavior onSelected(
116            SerializableBiConsumer<AjaxRequestTarget, List<FileDescription>> select)
117    {
118        Args.notNull(select, "select");
119
120        return new FilesSelectedBehavior()
121        {
122            private static final long serialVersionUID = 1L;
123
124            @Override
125            protected void onSelected(AjaxRequestTarget target, List<FileDescription> fileDescriptions) {
126                select.accept(target, fileDescriptions);
127            }
128        };
129    }
130}