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.extensions.ajax.markup.html.form.upload; 018 019import java.util.Formatter; 020 021import org.apache.wicket.Application; 022import org.apache.wicket.IInitializer; 023import org.apache.wicket.MarkupContainer; 024import org.apache.wicket.core.request.handler.IPartialPageRequestHandler; 025import org.apache.wicket.markup.head.CssHeaderItem; 026import org.apache.wicket.markup.head.IHeaderResponse; 027import org.apache.wicket.markup.head.JavaScriptHeaderItem; 028import org.apache.wicket.markup.head.OnDomReadyHeaderItem; 029import org.apache.wicket.markup.html.WebMarkupContainer; 030import org.apache.wicket.markup.html.form.Form; 031import org.apache.wicket.markup.html.form.upload.FileUploadField; 032import org.apache.wicket.markup.html.panel.Panel; 033import org.apache.wicket.model.StringResourceModel; 034import org.apache.wicket.request.resource.CssResourceReference; 035import org.apache.wicket.request.resource.JavaScriptResourceReference; 036import org.apache.wicket.request.resource.ResourceReference; 037import org.apache.wicket.request.resource.SharedResourceReference; 038import org.apache.wicket.resource.CoreLibrariesContributor; 039import org.apache.wicket.util.lang.Args; 040 041/** 042 * A panel to show the progress of an HTTP upload. 043 * <p> 044 * Note: For this to work upload progress monitoring must be enabled in the wicket application. 045 * Example: 046 * 047 * <pre> 048 * <code> 049 * public class App extends WebApplication { 050 * 051 * @Override 052 * protected void init() { 053 * super.init(); 054 * 055 * <b>getApplicationSettings().setUploadProgressUpdatesEnabled(true);</b> // <-- 056 * } 057 * } 058 * </code> 059 * </pre> 060 * 061 * For customizing starting text see {@link #RESOURCE_STARTING}. 062 * 063 * Implementation detail: Despite being located in an Ajax package, the progress communication is 064 * not done via Ajax but with an IFrame instead due to a bug in Webkit based browsers, see 065 * WICKET-3202. 066 * 067 * @author Andrew Lombardi 068 */ 069public class UploadProgressBar extends Panel 070{ 071 /** 072 * Resource key used to retrieve starting message for. 073 * 074 * Example: UploadProgressBar.starting=Upload starting... 075 */ 076 public static final String RESOURCE_STARTING = "UploadProgressBar.starting"; 077 078 /** 079 * Initializer for this component; binds static resources. 080 */ 081 public final static class ComponentInitializer implements IInitializer 082 { 083 @Override 084 public void init(final Application application) 085 { 086 // register the upload status resource 087 application.getSharedResources().add(RESOURCE_NAME, new UploadStatusResource()); 088 } 089 090 @Override 091 public String toString() 092 { 093 return "UploadProgressBar initializer"; 094 } 095 096 @Override 097 public void destroy(final Application application) 098 { 099 } 100 } 101 102 private static final ResourceReference JS = new JavaScriptResourceReference( 103 UploadProgressBar.class, "progressbar.js"); 104 105 private static final ResourceReference CSS = new CssResourceReference( 106 UploadProgressBar.class, "UploadProgressBar.css"); 107 108 private static final String RESOURCE_NAME = UploadProgressBar.class.getName(); 109 110 private static final long serialVersionUID = 1L; 111 112 private Form<?> form; 113 114 private MarkupContainer statusDiv; 115 116 private MarkupContainer barDiv; 117 118 private final FileUploadField uploadField; 119 120 /** 121 * Constructor that will display the upload progress bar for every submit of the given form. 122 * 123 * @param id 124 * component id (not null) 125 * @param uploadField 126 * the file upload field to check for a file upload, or null to display the upload 127 * field for every submit of the given form 128 */ 129 public UploadProgressBar(final String id, final FileUploadField uploadField) 130 { 131 super(id); 132 133 this.uploadField = uploadField; 134 if (uploadField != null) 135 { 136 uploadField.setOutputMarkupId(true); 137 } 138 139 setRenderBodyOnly(true); 140 } 141 142 143 144 /** 145 * Constructor that will display the upload progress bar for every submit of the given form. 146 * 147 * @param id 148 * component id (not null) 149 * @param form 150 * form that will be submitted (not null) 151 */ 152 public UploadProgressBar(final String id, final Form<?> form) 153 { 154 this(id, form, null); 155 } 156 157 /** 158 * Constructor that will display the upload progress bar for submissions of the given form, that 159 * include a file upload in the given file upload field; i.e. if the user did not select a file 160 * in the given file upload field, the progess bar is not displayed. 161 * 162 * @param id 163 * component id (not null) 164 * @param form 165 * form that is submitted (not null) 166 * @param uploadField 167 * the file upload field to check for a file upload, or null to display the upload 168 * field for every submit of the given form 169 */ 170 public UploadProgressBar(final String id, final Form<?> form, final FileUploadField uploadField) 171 { 172 super(id); 173 174 this.uploadField = uploadField; 175 if (uploadField != null) 176 { 177 uploadField.setOutputMarkupId(true); 178 } 179 180 this.form = Args.notNull(form, "form"); 181 form.setOutputMarkupId(true); 182 183 setRenderBodyOnly(true); 184 } 185 186 @Override 187 protected void onInitialize() 188 { 189 super.onInitialize(); 190 Form<?> form = getCallbackForm(); 191 if (form != null) { 192 form.setOutputMarkupId(true); 193 } 194 195 barDiv = newBarComponent("bar"); 196 add(barDiv); 197 198 statusDiv = newStatusComponent("status"); 199 add(statusDiv); 200 } 201 202 /** 203 * Creates a component for the status text 204 * 205 * @param id 206 * The component id 207 * @return the status component 208 */ 209 protected MarkupContainer newStatusComponent(String id) 210 { 211 WebMarkupContainer status = new WebMarkupContainer(id); 212 status.setOutputMarkupId(true); 213 return status; 214 } 215 216 /** 217 * Creates a component for the bar 218 * 219 * @param id 220 * The component id 221 * @return the bar component 222 */ 223 protected MarkupContainer newBarComponent(String id) 224 { 225 WebMarkupContainer bar = new WebMarkupContainer(id); 226 bar.setOutputMarkupId(true); 227 return bar; 228 } 229 230 /** 231 * Override this to provide your own CSS, or return null to avoid including the default. 232 * 233 * @return ResourceReference for your CSS. 234 */ 235 protected ResourceReference getCss() 236 { 237 return CSS; 238 } 239 240 /** 241 * {@inheritDoc} 242 */ 243 @Override 244 public void renderHead(final IHeaderResponse response) 245 { 246 super.renderHead(response); 247 248 CoreLibrariesContributor.contributeAjax(getApplication(), response); 249 response.render(JavaScriptHeaderItem.forReference(JS)); 250 ResourceReference css = getCss(); 251 if (css != null) 252 { 253 response.render(CssHeaderItem.forReference(css)); 254 } 255 256 ResourceReference ref = new SharedResourceReference(RESOURCE_NAME); 257 258 final String uploadFieldId = (uploadField == null) ? "null" : ("'" + uploadField.getMarkupId() + "'"); 259 260 final String status = new StringResourceModel(RESOURCE_STARTING, this, null).getString(); 261 262 CharSequence url = form != null ? urlFor(ref, UploadStatusResource.newParameter(getPage().getId())) : 263 urlFor(ref, UploadStatusResource.newParameter(uploadField.getMarkupId())); 264 265 StringBuilder builder = new StringBuilder(128); 266 Formatter formatter = new Formatter(builder); 267 268 Form<?> form = getCallbackForm(); 269 270 formatter.format(getVarName() + " = new Wicket.WUPB(%s, '%s', '%s', '%s', %s, '%s', %s);", 271 form != null ? "'" + form.getMarkupId() + "'" : "null", statusDiv.getMarkupId(), barDiv.getMarkupId(), url, uploadFieldId, 272 status, getOnProgressUpdatedCallBack()); 273 response.render(OnDomReadyHeaderItem.forScript(builder.toString())); 274 } 275 276 /** 277 * Allows to pass a JavaScript function that is called when progress in updated. 278 * 279 * @return A JavaScript function. 280 */ 281 protected String getOnProgressUpdatedCallBack() 282 { 283 return "function(percent) {}"; 284 } 285 286 private String getVarName() { 287 return "window.upb_" + barDiv.getMarkupId(); 288 } 289 290 291 public void start(IPartialPageRequestHandler handler) 292 { 293 handler.appendJavaScript(getVarName() + ".start();"); 294 } 295 296 /** 297 * Form on where will be installed the JavaScript callback to present the progress bar. 298 * 299 * @return form 300 */ 301 private Form<?> getCallbackForm() 302 { 303 if (form == null) 304 { 305 return null; 306 } 307 return form.getRootForm(); 308 } 309}