Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/util/ImageInfo.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/util/ImageInfo.java (revision ) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/util/ImageInfo.java (revision ) @@ -0,0 +1,186 @@ +/* + * SimpleImageInfo.java + * + * @version 0.1 + * @author Jaimon Mathew + * + * A Java class to determine image width, height and MIME types for a number of image file formats without loading the whole image data. + * + * Revision history + * 0.1 - 29/Jan/2011 - Initial version created + * + * ------------------------------------------------------------------------------- + + This code is licensed 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.hippoecm.frontend.plugins.yui.upload.util; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +@SuppressWarnings("all") +public class ImageInfo { + private int height; + private int width; + private String mimeType; + + private ImageInfo() { + + } + + public ImageInfo(File file) throws IOException { + InputStream is = new FileInputStream(file); + try { + processStream(is); + } finally { + is.close(); + } + } + + public ImageInfo(InputStream is) throws IOException { + processStream(is); + } + + public ImageInfo(byte[] bytes) throws IOException { + InputStream is = new ByteArrayInputStream(bytes); + try { + processStream(is); + } finally { + is.close(); + } + } + + private void processStream(InputStream is) throws IOException { + int c1 = is.read(); + int c2 = is.read(); + int c3 = is.read(); + + mimeType = null; + width = height = -1; + + if (c1 == 'G' && c2 == 'I' && c3 == 'F') { // GIF + is.skip(3); + width = readInt(is, 2, false); + height = readInt(is, 2, false); + mimeType = "image/gif"; + } else if (c1 == 0xFF && c2 == 0xD8) { // JPG + while (c3 == 255) { + int marker = is.read(); + int len = readInt(is, 2, true); + if (marker == 192 || marker == 193 || marker == 194) { + is.skip(1); + height = readInt(is, 2, true); + width = readInt(is, 2, true); + mimeType = "image/jpeg"; + break; + } + is.skip(len - 2); + c3 = is.read(); + } + } else if (c1 == 137 && c2 == 80 && c3 == 78) { // PNG + is.skip(15); + width = readInt(is, 2, true); + is.skip(2); + height = readInt(is, 2, true); + mimeType = "image/png"; + } else if (c1 == 66 && c2 == 77) { // BMP + is.skip(15); + width = readInt(is, 2, false); + is.skip(2); + height = readInt(is, 2, false); + mimeType = "image/bmp"; + } else { + int c4 = is.read(); + if ((c1 == 'M' && c2 == 'M' && c3 == 0 && c4 == 42) + || (c1 == 'I' && c2 == 'I' && c3 == 42 && c4 == 0)) { //TIFF + boolean bigEndian = c1 == 'M'; + int ifd = 0; + int entries; + ifd = readInt(is, 4, bigEndian); + is.skip(ifd - 8); + entries = readInt(is, 2, bigEndian); + for (int i = 1; i <= entries; i++) { + int tag = readInt(is, 2, bigEndian); + int fieldType = readInt(is, 2, bigEndian); + long count = readInt(is, 4, bigEndian); + int valOffset; + if ((fieldType == 3 || fieldType == 8)) { + valOffset = readInt(is, 2, bigEndian); + is.skip(2); + } else { + valOffset = readInt(is, 4, bigEndian); + } + if (tag == 256) { + width = valOffset; + } else if (tag == 257) { + height = valOffset; + } + if (width != -1 && height != -1) { + mimeType = "image/tiff"; + break; + } + } + } + } + if (mimeType == null) { + throw new IOException("Unsupported image type"); + } + } + + private int readInt(InputStream is, int noOfBytes, boolean bigEndian) throws IOException { + int ret = 0; + int sv = bigEndian ? ((noOfBytes - 1) * 8) : 0; + int cnt = bigEndian ? -8 : 8; + for (int i = 0; i < noOfBytes; i++) { + ret |= is.read() << sv; + sv += cnt; + } + return ret; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public String getMimeType() { + return mimeType; + } + + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + + @Override + public String toString() { + return "MIME Type : " + mimeType + "\t Width : " + width + "\t Height : " + height; + } +} Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/UploadValidationPlugin.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/UploadValidationPlugin.java (revision ) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/UploadValidationPlugin.java (revision ) @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2012 Hippo. + * + * Licensed 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.hippoecm.frontend.plugins.yui.upload.validation; + +import org.hippoecm.frontend.plugin.IPluginContext; +import org.hippoecm.frontend.plugin.Plugin; +import org.hippoecm.frontend.plugin.config.IPluginConfig; + +public class UploadValidationPlugin extends Plugin { + final static String SVN_ID = "$Id$"; + + public UploadValidationPlugin(IPluginContext context, IPluginConfig config) { + super(context, config); + + final String id = config.getString(IUploadValidationService.VALIDATE_ID, "upload.validation.service"); + context.registerService(createValidator(), id); + } + + protected IUploadValidationService createValidator() { + return new DefaultUploadValidationService(getPluginConfig()); + } + +} Index: gallery/repository/src/main/resources/gallery-namespace.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- gallery/repository/src/main/resources/gallery-namespace.xml (revision 34000) +++ gallery/repository/src/main/resources/gallery-namespace.xml (revision ) @@ -175,6 +175,9 @@ service.gallery.processor + + service.gallery.image.validation + 154px Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/inc/hippo/281/upload/upload.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/inc/hippo/281/upload/upload.js (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/inc/hippo/281/upload/upload.js (revision ) @@ -4,17 +4,53 @@ * Provides a singleton upload helper *

* @namespace YAHOO.hippo - * @requires yahoo, dom, hippoajax, uploader, progressbar, datatable, datasource, button, ajaxindicator, hashmap + * @requires yahoo, dom, hippoajax, uploader, progressbar, datatable, datasource, button, container, ajaxindicator, hashmap * @module upload * @beta */ YAHOO.namespace('hippo'); +YAHOO.util.Event.throwErrors = true; if (!YAHOO.hippo.Upload) { (function() { var Dom = YAHOO.util.Dom, Lang = YAHOO.lang; + YAHOO.hippo.BytesToHumanReadable = function(filesize) { + var number_format = function(number, decimals, dec_point, thousands_sep) { + // http://kevin.vanzonneveld.net + // + original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfix by: Michael White (http://crestidg.com) + // + bugfix by: Benjamin Lupton + // + bugfix by: Allan Jensen (http://www.winternet.no) + // + revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) + // * example 1: number_format(1234.5678, 2, '.', ''); + // * returns 1: 1234.57 + + var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals; + var d = dec_point == undefined ? "," : dec_point; + var t = thousands_sep == undefined ? "." : thousands_sep, s = n < 0 ? "-" : ""; + var i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0; + + return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : ""); + }; + if (filesize >= 1073741824) { + filesize = number_format(filesize / 1073741824, 2, '.', '') + ' Gb'; + } else { + if (filesize >= 1048576) { + filesize = number_format(filesize / 1048576, 2, '.', '') + ' Mb'; + } else { + if (filesize >= 1024) { + filesize = number_format(filesize / 1024, 0) + ' Kb'; + } else { + filesize = number_format(filesize, 0) + ' bytes'; + } + } + } + return filesize; + }; + YAHOO.hippo.UploadImpl = function() { //YAHOO.widget.Uploader.SWFURL = "http://yui.yahooapis.com/2.8.1/build/uploader/assets/uploader.swf"; this.latest = null; @@ -81,14 +117,14 @@ YAHOO.widget.Uploader.SWFURL = config.flashUrl; - var root = new YAHOO.util.Element(Dom.get(id)); + this.root = new YAHOO.util.Element(Dom.get(id)); this.elements.uiElements = new YAHOO.util.Element(document.createElement('div')); Dom.setStyle(this.elements.uiElements, 'display', 'inline'); - root.appendChild(this.elements.uiElements); + this.root.appendChild(this.elements.uiElements); this.elements.datatableContainer = new YAHOO.util.Element(document.createElement('div')); Dom.addClass(this.elements.datatableContainer, 'dataTableContainer'); - root.appendChild(this.elements.datatableContainer); + this.root.appendChild(this.elements.datatableContainer); this.elements.uploaderContainer = new YAHOO.util.Element(document.createElement('div')); this.elements.uiElements.appendChild(this.elements.uploaderContainer); @@ -111,12 +147,13 @@ Dom.setStyle(this.elements.uploaderOverlay, 'width', config.buttonWidth == null || config.buttonWidth == "" ? "155px" : config.buttonWidth); Dom.setStyle(this.elements.uploaderOverlay, 'height', config.buttonHeight == null || config.buttonHeight == "" ? "26px" : config.buttonHeight); - this.fileList = null; + this.dataArr = []; + this.numberOfUploads = 0; this.initializeUploader(); - YAHOO.hippo.HippoAjax.registerDestroyFunction(root, this.destroy, this); + YAHOO.hippo.HippoAjax.registerDestroyFunction(this.root, this.destroy, this); }; YAHOO.hippo.UploadWidget.prototype = { @@ -139,7 +176,8 @@ }, upload : function() { - if(this.fileList != null) { + //if(this.fileList != null) { + if(this.dataArr.length > 0) { if(this.config.hideBrowseDuringUpload) { //Dom.setStyle(this.elements.uploaderContainer, 'display', 'none'); } @@ -163,13 +201,54 @@ }, onFileSelect : function(event) { + //If feedback from a previous upload is still shown, hide it. + Dom.getElementsByClassName('upload-feedback-panel', 'div', this.root.parentNode, function(el) { + Dom.setStyle(el, 'display', 'none'); + }); + + if('fileList' in event && event.fileList != null) { - this.fileList = event.fileList; - this._createDatatable(this.fileList); + this.dataArr = []; + var errors = []; + + for (var fileName in event.fileList) { + if(YAHOO.lang.hasOwnProperty(event.fileList, fileName)) { + var file = event.fileList[fileName]; + + if(this.config.clientSideValidation) { + var result = this._validate(file); + if(result === true) { + file["progress"] = -1; + this.dataArr.unshift(file); + } else { + this.uploader.removeFile(file.id); + errors.push(result); } + } else { + file["progress"] = -1; + this.dataArr.unshift(file); + } + } + } + if(errors.length > 0) { + var msg = ''; + for(var i=0; i'; + } + + var dialog = this._createDialog(msg, function() { + dialog.hide(); + dialog.destroy(); + }); + dialog.show(); + } + + this._createDatatable(); + } if(this.config.uploadAfterSelect === true) { this.upload(); } + this.hasError = errors.length > 0; }, onUploadStart : function(event) { @@ -238,7 +317,7 @@ //Dom.setStyle(this.elements.uploaderContainer, 'display', 'block'); } - var url = this.config.callbackUrl + "&finished=true"; + var url = this.config.callbackUrl + "&finished=true&hasError=" + this.hasError; if(this.scrollData != null) { url = url + this.scrollData; this.scrollData = null; @@ -284,8 +363,12 @@ if (this.config.fileExtensions != null && this.config.fileExtensions.length > 0) { var allowedExtensions = ''; for (var i = 0; i < this.config.fileExtensions.length; ++i) { - allowedExtensions += this.config.fileExtensions[i] + ';'; + var ext = this.config.fileExtensions[i]; + if(ext.indexOf("*.") != 0) { + ext = "*." + ext; } + allowedExtensions += ext + ';'; + } // Apply new set of file filters to the uploader. this.uploader.setFileFilters(new Array({description:"Files", extensions:allowedExtensions})); } @@ -330,6 +413,14 @@ this.datatable.deleteRow(oRecord._sId); }, + _validate: function(file) { + if(file.size > this.config.maxFileSize) { + var msg = this.config.translations['filesize.too.large']; + return msg.replace('{0}', file.name).replace('{1}', YAHOO.hippo.BytesToHumanReadable(file.size)).replace('{2}', YAHOO.hippo.BytesToHumanReadable(this.config.maxFileSize)); + } + return true; + }, + _getRecordById : function(id) { var recordSet = this.datatable.getRecordSet(); for (var j = 0; j < recordSet.getLength(); j++) { @@ -341,14 +432,34 @@ return null; }, - _createDatatable : function(entries) { - this.dataArr = []; - for(var i in entries) { - var entry = entries[i]; - entry["progress"] = -1; - this.dataArr.unshift(entry); + _createDialog : function(msg, okHandler) { + var dialog = new YAHOO.widget.SimpleDialog("Dialog" + id, + { width : "380px", + fixedcenter : true, + visible : false, + close: true, + draggable: false, + constraintoviewport : true, + icon: YAHOO.widget.SimpleDialog.ICON_WARN, + buttons : [ + { text:"OK", handler:okHandler, isDefault:true } + ] } + ); + dialog.setHeader(this.config.translations['error.dialog.header']); + dialog.cfg.setProperty('text', msg); + dialog.render(this.root); + Dom.addClass(this.root, 'yui-skin-sam'); + + return dialog; + }, + + _createDatatable : function() { + if(this.dataArr.length == 0) { + return; + } + var nameWidth = 305; if(this.dataArr.length > 10) { nameWidth -= YAHOO.hippo.HippoAjax.getScrollbarWidth(); @@ -392,14 +503,16 @@ selectionMode:"single", width: '440px', height: '236px', - sortedBy: sortedBy + sortedBy: sortedBy, + MSG_EMPTY: '' }); } else { this.datatable = new YAHOO.widget.DataTable( this.elements.datatableContainer, myColumnDefs, myDataSource, { selectionMode:"single", - sortedBy: sortedBy + sortedBy: sortedBy, + MSG_EMPTY: '' }); } }, @@ -421,39 +534,7 @@ }, _bytesFormatter : function(elLiner, oRecord, oColumn, oData) { - var number_format = function(number, decimals, dec_point, thousands_sep) { - // http://kevin.vanzonneveld.net - // + original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) - // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - // + bugfix by: Michael White (http://crestidg.com) - // + bugfix by: Benjamin Lupton - // + bugfix by: Allan Jensen (http://www.winternet.no) - // + revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) - // * example 1: number_format(1234.5678, 2, '.', ''); - // * returns 1: 1234.57 - - var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals; - var d = dec_point == undefined ? "," : dec_point; - var t = thousands_sep == undefined ? "." : thousands_sep, s = n < 0 ? "-" : ""; - var i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0; - - return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : ""); - }; - var filesize = oData; - if (filesize >= 1073741824) { - filesize = number_format(filesize / 1073741824, 2, '.', '') + ' Gb'; - } else { - if (filesize >= 1048576) { - filesize = number_format(filesize / 1048576, 2, '.', '') + ' Mb'; - } else { - if (filesize >= 1024) { - filesize = number_format(filesize / 1024, 0) + ' Kb'; - } else { - filesize = number_format(filesize, 0) + ' bytes'; - } - } - } - elLiner.innerHTML = filesize; + elLiner.innerHTML = YAHOO.hippo.BytesToHumanReadable(oData); }, _removeFormatter : function(elLiner, oRecord, oColumn, oData) { Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidget.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidget.java (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidget.java (revision ) @@ -15,27 +15,38 @@ */ package org.hippoecm.frontend.plugins.yui.upload; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + import org.apache.wicket.Component; import org.apache.wicket.Page; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.IAjaxIndicatorAware; import org.apache.wicket.behavior.IBehavior; +import org.apache.wicket.markup.html.CSSPackageResource; import org.apache.wicket.markup.html.form.upload.FileUpload; import org.apache.wicket.markup.html.panel.EmptyPanel; +import org.apache.wicket.markup.html.panel.FeedbackPanel; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.StringResourceModel; +import org.apache.wicket.util.value.ValueMap; import org.hippoecm.frontend.plugins.yui.flash.FlashVersion; import org.hippoecm.frontend.plugins.yui.upload.ajax.AjaxMultiFileUploadComponent; import org.hippoecm.frontend.plugins.yui.upload.ajax.AjaxMultiFileUploadSettings; import org.hippoecm.frontend.plugins.yui.upload.multifile.MultiFileUploadComponent; +import org.hippoecm.frontend.plugins.yui.upload.validation.DefaultUploadValidationService; +import org.hippoecm.frontend.plugins.yui.upload.validation.IUploadValidationService; import org.hippoecm.frontend.plugins.yui.webapp.WebAppBehavior; +import org.hippoecm.frontend.validation.IValidationResult; +import org.hippoecm.frontend.validation.ValidationException; +import org.hippoecm.frontend.validation.Violation; -import java.util.Collection; - /** - * Widget for uploading files. This widget allows both flash and non-flash uploads based on the configuration. - * By default the flash upload is used. For more configuration options please take a look at - * {@link FileUploadWidgetSettings}. + * Widget for uploading files. This widget allows both flash and non-flash uploads based on the configuration. By + * default the flash upload is used. For more configuration options please take a look at {@link + * FileUploadWidgetSettings}. */ public class FileUploadWidget extends Panel { @@ -46,34 +57,54 @@ private static final String COMPONENT_ID = "component"; - private FileUploadWidgetSettings settings; private Panel panel; + private FileUploadWidgetSettings settings; + private IUploadValidationService validator; + private List violations; private final static FlashVersion VALID_FLASH = new FlashVersion(9, 0, 45); private FlashVersion detectedFlash; public FileUploadWidget(String id, FileUploadWidgetSettings settings) { - super(id); + this(id, settings, null); + } + public FileUploadWidget(String id, FileUploadWidgetSettings settings, IUploadValidationService validator) { + super(id); setOutputMarkupId(true); + + add(CSSPackageResource.getHeaderContribution(FileUploadWidget.class, "FileUploadWidget.css")); + + this.violations = new LinkedList(); this.settings = settings; - add(panel = new EmptyPanel(COMPONENT_ID)); + + this.validator = validator; + if (validator == null) { + ValueMap params = new ValueMap(); + params.put(DefaultUploadValidationService.EXTENSIONS_ALLOWED, settings.getFileExtensions()); + this.validator = new DefaultUploadValidationService(params); + } else { + settings.setFileExtensions(validator.getAllowedExtensions()); - } + } - protected void onFileUpload(FileUpload fileUpload) { + add(panel = new EmptyPanel(COMPONENT_ID)); + add(new FeedbackPanel("feedbackPanel")); } + /** + * Check if the client supports flash by looking up the WebAppBehavior in the page which test the client for flash + * on the first load and saves the version. + */ @Override protected void onBeforeRender() { super.onBeforeRender(); - if(settings.isFlashUploadEnabled()) { + if (settings.isFlashUploadEnabled()) { if (detectedFlash == null) { Page page = getPage(); for (IBehavior behavior : page.getBehaviors()) { if (behavior instanceof WebAppBehavior) { - WebAppBehavior webapp = (WebAppBehavior) behavior; - detectedFlash = webapp.getFlash(); + detectedFlash = ((WebAppBehavior) behavior).getFlash(); } } } @@ -87,112 +118,144 @@ } /** - * Detect if flash is installed and if the correct version of the flash plugin is found. - * @return true if flash and the correct version is detected, false otherwise + * Traverse up the component tree in search for an IAjaxIndicatorAware component is used to indicate a busy state + * while uploading. + * + * @return IAjaxIndicatorAware component or null of not found */ - public boolean isFlashUpload() { - return detectedFlash != null && detectedFlash.isValid(VALID_FLASH); + protected String getAjaxIndicatorId() { + Component c = this; + while (c != null) { + if (IAjaxIndicatorAware.class.isAssignableFrom(c.getClass())) { + return ((IAjaxIndicatorAware) c).getAjaxIndicatorMarkupId(); - } + } + c = c.getParent(); + } + return null; + } + protected void renderFlashUpload() { - AjaxMultiFileUploadSettings ajaxMultiFileUploadSettings = new AjaxMultiFileUploadSettings(); - ajaxMultiFileUploadSettings.setFileExtensions(settings.getFileExtensions()); - ajaxMultiFileUploadSettings.setAllowMultipleFiles(settings.getMaxNumberOfFiles() > 1); - ajaxMultiFileUploadSettings.setUploadAfterSelect(settings.isAutoUpload()); - ajaxMultiFileUploadSettings.setClearAfterUpload(settings.isClearAfterUpload()); - ajaxMultiFileUploadSettings.setClearTimeout(settings.getClearTimeout()); - ajaxMultiFileUploadSettings.setHideBrowseDuringUpload(settings.isHideBrowseDuringUpload()); - ajaxMultiFileUploadSettings.setAjaxIndicatorId(getAjaxIndicatorId()); - ajaxMultiFileUploadSettings.setButtonWidth(settings.getButtonWidth()); - replace(panel = new AjaxMultiFileUploadComponent(COMPONENT_ID, ajaxMultiFileUploadSettings) { + AjaxMultiFileUploadSettings ajaxSettings = new AjaxMultiFileUploadSettings(); + ajaxSettings.setFileExtensions(settings.getFileExtensions()); + ajaxSettings.setAllowMultipleFiles(settings.getMaxNumberOfFiles() > 1); + ajaxSettings.setUploadAfterSelect(settings.isAutoUpload()); + ajaxSettings.setClearAfterUpload(settings.isClearAfterUpload()); + ajaxSettings.setClearTimeout(settings.getClearTimeout()); + ajaxSettings.setHideBrowseDuringUpload(settings.isHideBrowseDuringUpload()); + ajaxSettings.setAjaxIndicatorId(getAjaxIndicatorId()); + ajaxSettings.setButtonWidth(settings.getButtonWidth()); + //Allow the flash uploader to validate files by size on the client before uploading starts. + ajaxSettings.setClientSideValidation(true); + ajaxSettings.setMaxFileSize(validator.getMaxFileSize().bytes()); + + replace(panel = new AjaxMultiFileUploadComponent(COMPONENT_ID, ajaxSettings) { + @Override protected void onFileUpload(FileUpload fileUpload) { - FileUploadWidget.this.onFileUpload(fileUpload); + handleFileUpload(fileUpload); } @Override - protected void onFinish(AjaxRequestTarget target) { - FileUploadWidget.this.onFinishAjaxUpload(target); + protected void onFinish(AjaxRequestTarget target, boolean hasError) { + if (hasViolations()) { + handleViolations(); + target.addComponent(FileUploadWidget.this); - } + } - - @Override - protected void onUploadSuccess() { + FileUploadWidget.this.onFinishAjaxUpload(target, hasError); } + }); } - protected void renderJavascriptUpload() { - int max = settings.isAutoUpload() ? 1 : settings.getMaxNumberOfFiles(); - replace(panel = new MultiFileUploadComponent(COMPONENT_ID, max)); + /** + * Detect if flash is installed and if the correct version of the flash plugin is found. + * + * @return true if flash and the correct version is detected, false otherwise + */ + public boolean isFlashUpload() { + return detectedFlash != null && detectedFlash.isValid(VALID_FLASH); } - protected String getAjaxIndicatorId() { - Component c = this; - while (c != null) { - if (IAjaxIndicatorAware.class.isAssignableFrom(c.getClass())) { - return ((IAjaxIndicatorAware) c).getAjaxIndicatorMarkupId(); + /** + * Components that embed a FileUploadWidget might have their own actions for triggering the upload, this method + * returns the javascript call to initiate it. + * + * @return Javascript call that start the ajax upload + */ + public String getStartAjaxUploadScript() { + return "YAHOO.hippo.Upload.upload();"; - } + } - c = c.getParent(); + + protected void onFinishAjaxUpload(AjaxRequestTarget target, boolean hasError) { + - } + } - return null; - } - protected void onFinishAjaxUpload(AjaxRequestTarget target) { + protected void renderJavascriptUpload() { + int max = settings.isAutoUpload() ? 1 : settings.getMaxNumberOfFiles(); + replace(panel = new MultiFileUploadComponent(COMPONENT_ID, max)); } + public void handleNonFlashSubmit() { if (!isFlashUpload()) { Collection uploads = ((MultiFileUploadComponent) panel).getUploads(); if (uploads != null) { for (FileUpload upload : uploads) { - if (fileUploadIsValid(upload)) { - onFileUpload(upload); + handleFileUpload(upload); - } - } + } + } + handleViolations(); - } - } + } + } - } - private boolean fileUploadIsValid(FileUpload upload) { - if (settings.getFileExtensions() == null || settings.getFileExtensions().length == 0) { - return true; + private void handleViolations() { + if (violations.size() > 0) { + List errors = new ArrayList(violations.size()); + for (Violation v : violations) { + errors.add(new StringResourceModel(v.getMessageKey(), this, null, v.getParameters()).getString()); - } + } - - String fileName = upload.getClientFileName(); - int dotIndex = fileName.lastIndexOf('.'); - if (dotIndex == -1 || dotIndex == fileName.length() - 1) { - String msg = new StringResourceModel("extension.not.found", this, null, - new Object[]{fileName, getReadableFileExtensions()}).getObject(); - error(msg); - return false; + renderErrors(errors); + violations.clear(); } - String uploadExt = fileName.substring(dotIndex + 1).toLowerCase(); - for (String extension : settings.getFileExtensions()) { - extension = extension.toLowerCase(); - int extDotIndex = extension.lastIndexOf('.'); - if (extDotIndex > -1) { - extension = extension.substring(extDotIndex + 1); - } + } - if (uploadExt.equals(extension)) { - return true; + + protected void renderErrors(final List errors) { + for (String error : errors) { + error(error); - } - } + } + } - String msg = new StringResourceModel("extension.not.allowed", this, null, - new Object[]{getReadableFileExtensions()}).getObject(); - error(msg); - return false; + + protected boolean hasViolations() { + return violations.size() > 0; } - private String getReadableFileExtensions() { - StringBuilder sb = new StringBuilder(); - for (String extension : settings.getFileExtensions()) { - sb.append(extension).append(" "); + private void handleFileUpload(FileUpload fileUpload) { + try { + validator.validate(fileUpload); + IValidationResult result = validator.getValidationResult(); + + if (result.isValid()) { + onFileUpload(fileUpload); + } else { + violations.addAll(result.getViolations()); - } + } - return sb.toString(); + //delete uploaded file after it is processed + fileUpload.delete(); + + } catch (ValidationException e) { + //TODO: log + e.printStackTrace(); - } + } + } - public String getStartAjaxUploadScript() { - return "YAHOO.hippo.Upload.upload();"; + /** + * Hook method for subclasses to handle an upload once it has passed validation. + * + * @param fileUpload The uploaded file + */ + protected void onFileUpload(FileUpload fileUpload) { } + } Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/ImageUploadValidationPlugin.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/ImageUploadValidationPlugin.java (revision ) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/ImageUploadValidationPlugin.java (revision ) @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2012 Hippo. + * + * Licensed 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.hippoecm.frontend.plugins.yui.upload.validation; + +import org.hippoecm.frontend.plugin.IPluginContext; +import org.hippoecm.frontend.plugin.config.IPluginConfig; + +public class ImageUploadValidationPlugin extends UploadValidationPlugin { + + public ImageUploadValidationPlugin(IPluginContext context, IPluginConfig config) { + super(context, config); + } + + @Override + protected IUploadValidationService createValidator() { + return new ImageUploadValidationService(getPluginConfig()); + } + +} Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/MultiFileUploadDialog.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/MultiFileUploadDialog.java (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/MultiFileUploadDialog.java (revision ) @@ -22,7 +22,9 @@ import org.apache.wicket.model.Model; import org.apache.wicket.util.value.IValueMap; import org.hippoecm.frontend.dialog.AbstractDialog; +import org.hippoecm.frontend.plugin.IPluginContext; import org.hippoecm.frontend.plugin.config.IPluginConfig; +import org.hippoecm.frontend.plugins.yui.upload.validation.IUploadValidationService; /** * A multi file upload dialog that can be configured by means of the {@link FileUploadWidgetSettings}. @@ -36,7 +38,7 @@ private FileUploadWidget widget; private AjaxButton ajaxButton; - protected MultiFileUploadDialog(IPluginConfig pluginConfig) { + protected MultiFileUploadDialog(IPluginContext pluginContext, IPluginConfig pluginConfig) { setOutputMarkupId(true); setNonAjaxSubmit(); @@ -58,8 +60,11 @@ ajaxButton.setVisible(false); addButton(ajaxButton); + String serviceId = pluginConfig.getString(IUploadValidationService.VALIDATE_ID); + IUploadValidationService validator = pluginContext.getService(serviceId, IUploadValidationService.class); + FileUploadWidgetSettings settings = new FileUploadWidgetSettings(pluginConfig); - widget = new FileUploadWidget("uploadWidget", settings) { + widget = new FileUploadWidget("uploadWidget", settings, validator) { @Override protected void onFileUpload(FileUpload file) { @@ -67,9 +72,16 @@ } @Override - protected void onFinishAjaxUpload(AjaxRequestTarget target) { - MultiFileUploadDialog.super.handleSubmit(); + protected void onFinishAjaxUpload(AjaxRequestTarget target, boolean hasError) { +/* + if(hasError()) { + target.addComponent(MultiFileUploadDialog.this); - } + } +*/ + //if(!hasError) { + MultiFileUploadDialog.this.handleSubmit(); + // } + } @Override public void renderFlashUpload() { @@ -97,10 +109,7 @@ } } - @Override - protected String getAjaxIndicatorId() { - return super.getAjaxIndicatorId(); - } + }; add(widget); } Index: api/src/main/java/org/hippoecm/frontend/editor/plugins/resource/ResourceHelper.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/editor/plugins/resource/ResourceHelper.java (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/editor/plugins/resource/ResourceHelper.java (revision ) @@ -15,6 +15,18 @@ */ package org.hippoecm.frontend.editor.plugins.resource; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Calendar; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.ValueFactory; + import org.apache.commons.io.IOUtils; import org.apache.jackrabbit.JcrConstants; import org.apache.tika.config.TikaConfig; @@ -24,14 +36,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.*; -import java.util.Calendar; - -import javax.jcr.Node; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.ValueFactory; - /** * Resource helper for creating and validating nodes of type hippo:resource */ @@ -62,7 +66,7 @@ if (imageInfo.check()) { String imageInfoMimeType = imageInfo.getMimeType(); if (imageInfoMimeType == null) { - throw new ResourceException("impermissable image type content"); + throw new ResourceException("impermissible image type content"); } else { if (imageInfoMimeType.equals(MIME_IMAGE_PJPEG)) { imageInfoMimeType = MIME_IMAGE_JPEG; @@ -72,29 +76,29 @@ } } } else { - throw new ResourceException("impermissable image type content"); + throw new ResourceException("impermissible image type content"); } } else if (mimeType.equals(MIME_TYPE_PDF)) { String line; line = new BufferedReader(new InputStreamReader(resource.getProperty(JcrConstants.JCR_DATA).getStream())) .readLine().toUpperCase(); if (!line.startsWith("%PDF-")) { - throw new ResourceException("impermissable pdf type content"); + throw new ResourceException("impermissible pdf type content"); } } else if (mimeType.equals("application/postscript")) { String line; line = new BufferedReader(new InputStreamReader(resource.getProperty(JcrConstants.JCR_DATA).getStream())) .readLine().toUpperCase(); if (!line.startsWith("%!")) { - throw new ResourceException("impermissable postscript type content"); + throw new ResourceException("impermissible postscript type content"); } } else { // This method can be overridden to allow more such checks on content type. if such an override // wants to be really strict and not allow unknown content, the following thrown exception is to be included - // throw new ValueFormatException("impermissable unrecognized type content"); + // throw new ValueFormatException("impermissible unrecognized type content"); } } catch (IOException ex) { - throw new ResourceException("impermissable unknown type content"); + throw new ResourceException("impermissible unknown type content"); } } Index: gallery/repository/src/main/resources/hippoecm-extension.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- gallery/repository/src/main/resources/hippoecm-extension.xml (revision 34000) +++ gallery/repository/src/main/resources/hippoecm-extension.xml (revision ) @@ -316,5 +316,19 @@ 2007.0 + + + hippo:initializeitem + + + image-validator.xml + + + /hippo:configuration/hippo:frontend/cms/cms-services + + + 2008.0 + + Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidgetSettings.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidgetSettings.java (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidgetSettings.java (revision ) @@ -19,24 +19,18 @@ import org.hippoecm.frontend.plugin.config.IPluginConfig; /** - * Settings for file uploads. Currently allowed configurable settings are: - *
    - *
  • fileupload.flashEnabled = true for flash or false for javascript upload
  • - *
  • fileupload.maxItems = maximum allowed file uploads at the same time
  • - *
  • fileupload.allowedExtensions = allowed upload file extensions
  • + * Settings for file uploads. Currently allowed configurable settings are:
    • fileupload.flashEnabled = + * true for flash or false for javascript upload
    • fileupload.maxItems = maximum + * allowed file uploads at the same time
    • fileupload.allowedExtensions = allowed upload file extensions
    • - *
    • fileupload.autoUpload = if true the plugin will automatically upload the files
    • + *
    • fileupload.autoUpload = if true the plugin will automatically upload the files
    • - *
    • fileupload.buttonWidth = defines the width of the upload button
    • - *
    • fileupload.buttonHeight = defines the height of the upload button
    • - *
    • fileupload.clearAfterUpload = if true the dialog is cleared after all files are uploaded
    • - *
    • fileupload.clearTimeout = defines the timeout before clearing the dialog after the upload
    • - *
    • fileupload.hideBrowseDuringUpload = if true the browse button will be hidden during the upload
    • - *
    - * Backwards compatibility: - *
      - *
    • file.extensions = allowed upload file extensions
    • - *
    + *
  • fileupload.buttonWidth = defines the width of the upload button
  • fileupload.buttonHeight = defines the + * height of the upload button
  • fileupload.clearAfterUpload = if true the dialog is cleared after + * all files are uploaded
  • fileupload.clearTimeout = defines the timeout before clearing the dialog after the + * upload
  • fileupload.hideBrowseDuringUpload = if true the browse button will be hidden during the + * upload
  • fileupload.maxFileSize = maximum allowed filesize in bytes
Backwards compatibility:
    + *
  • file.extensions = allowed upload file extensions
*/ -public class FileUploadWidgetSettings implements IClusterable{ +public class FileUploadWidgetSettings implements IClusterable { @SuppressWarnings("unused") final static String SVN_ID = "$Id$"; @@ -61,10 +55,10 @@ private boolean hideBrowseDuringUpload; private String buttonWidth; private String buttonHeight; - private boolean flashUploadEnabled = true; + private boolean flashUploadEnabled = false; public FileUploadWidgetSettings() { - } + } public FileUploadWidgetSettings(IPluginConfig pluginConfig) { parsePluginConfig(pluginConfig); @@ -136,6 +130,7 @@ /** * Indicates if the upload widget should use Flash. + * * @return true if flash should be used, false otherwise */ public boolean isFlashUploadEnabled() { @@ -143,8 +138,9 @@ } /** - * If set to true (default) the upload plugin will use flash for file uploads, otherwise it will use a plain - * Javascript upload. + * If set to true (default) the upload plugin will use flash for file uploads, otherwise it will use a + * plain Javascript upload. + * * @param flashUploadEnabled boolean indicating if flash should be used for file uploads. */ public void setFlashUploadEnabled(boolean flashUploadEnabled) { @@ -152,39 +148,37 @@ } private void parsePluginConfig(final IPluginConfig pluginConfig) { - if(pluginConfig.containsKey(FILEUPLOAD_FLASH_ENABLED_SETTING)) { + if (pluginConfig.containsKey(FILEUPLOAD_FLASH_ENABLED_SETTING)) { this.flashUploadEnabled = pluginConfig.getAsBoolean(FILEUPLOAD_FLASH_ENABLED_SETTING); } - if(pluginConfig.containsKey(FILEUPLOAD_MAX_ITEMS_SETTING)) { + if (pluginConfig.containsKey(FILEUPLOAD_MAX_ITEMS_SETTING)) { this.maxNumberOfFiles = pluginConfig.getAsInteger(FILEUPLOAD_MAX_ITEMS_SETTING); } // for backwards compatibility - if(pluginConfig.containsKey(FILE_EXTENSIONS_SETTING)) { + if (pluginConfig.containsKey(FILE_EXTENSIONS_SETTING)) { this.fileExtensions = pluginConfig.getStringArray(FILE_EXTENSIONS_SETTING); } - if(pluginConfig.containsKey(FILEUPLOAD_ALLOWED_EXTENSIONS_SETTING)) { + if (pluginConfig.containsKey(FILEUPLOAD_ALLOWED_EXTENSIONS_SETTING)) { this.fileExtensions = pluginConfig.getStringArray(FILEUPLOAD_ALLOWED_EXTENSIONS_SETTING); } - - if(pluginConfig.containsKey(FILEUPLOAD_AUTOUPLOAD_SETTING)) { + if (pluginConfig.containsKey(FILEUPLOAD_AUTOUPLOAD_SETTING)) { this.autoUpload = pluginConfig.getAsBoolean(FILEUPLOAD_AUTOUPLOAD_SETTING); } - if(pluginConfig.containsKey(FILEUPLOAD_BUTTON_WIDTH)) { + if (pluginConfig.containsKey(FILEUPLOAD_BUTTON_WIDTH)) { this.buttonWidth = pluginConfig.getString(FILEUPLOAD_BUTTON_WIDTH); } - if(pluginConfig.containsKey(FILEUPLOAD_BUTTON_HEIGHT)) { + if (pluginConfig.containsKey(FILEUPLOAD_BUTTON_HEIGHT)) { this.buttonHeight = pluginConfig.getString(FILEUPLOAD_BUTTON_HEIGHT); } - if(pluginConfig.containsKey(FILEUPLOAD_CLEAR_AFTER_UPLOAD)) { + if (pluginConfig.containsKey(FILEUPLOAD_CLEAR_AFTER_UPLOAD)) { this.clearAfterUpload = pluginConfig.getAsBoolean(FILEUPLOAD_CLEAR_AFTER_UPLOAD); } - if(pluginConfig.containsKey(FILEUPLOAD_CLEAR_TIMEOUT)) { + if (pluginConfig.containsKey(FILEUPLOAD_CLEAR_TIMEOUT)) { this.clearTimeout = pluginConfig.getAsInteger(FILEUPLOAD_CLEAR_TIMEOUT); } - if(pluginConfig.containsKey(FILEUPLOAD_HIDE_BROWSE_DURING_UPLOAD)) { + if (pluginConfig.containsKey(FILEUPLOAD_HIDE_BROWSE_DURING_UPLOAD)) { this.hideBrowseDuringUpload = pluginConfig.getAsBoolean(FILEUPLOAD_HIDE_BROWSE_DURING_UPLOAD); } - } } Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/multifile/MultiFileUpload.css IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/multifile/MultiFileUpload.css (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/multifile/MultiFileUpload.css (revision ) @@ -28,7 +28,6 @@ overflow: auto; } - .hippo-window .hippo-dialog-container .wicket-mfu-row { border-bottom: 1px solid #F4F4F4; height: 24px; \ No newline at end of file Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/ajax/res/skin.css IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/ajax/res/skin.css (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/ajax/res/skin.css (revision ) @@ -1,63 +1,63 @@ .resource-values { - overflow: auto; + overflow: auto; } .selectFilesLink { - width: 244px; - height: 26px; - padding-top: 6px; - padding-left: 30px; - color: #333333; - background: url("select_upload_icononly.png") no-repeat; + width: 244px; + height: 26px; + padding-top: 6px; + padding-left: 30px; + color: #333333; + background: url("select_upload_icononly.png") no-repeat; } .selectFilesLink.rollover { - background: url("select_upload_icononly_hover.png") no-repeat; + background: url("select_upload_icononly_hover.png") no-repeat; } .yau-progressbar { - height: 5px; - background-color: #F00; + height: 5px; + background-color: #F00; } .yau-progressbar-container { - height: 5px; - width: 100px; - background-color: #CCC; + height: 5px; + width: 100px; + background-color: #CCC; } .yau-select-link-rollout { - color: #0000CC; - background-color: #FFFFFF; + color: #0000CC; + background-color: #FFFFFF; } .yau-select-link-rollover { - color: #000000; - background-color: #FFFFFF; + color: #000000; + background-color: #FFFFFF; } .dataTableContainer { - margin-top: 10px; + margin-top: 10px; } .dataTableContainer .remove_file { - background-image: url("delete-small-16.png"); - width: 16px; - height: 16px; - cursor: pointer; + background-image: url("delete-small-16.png"); + width: 16px; + height: 16px; + cursor: pointer; } .hippo-root .yui-dt td.yui-dt-last { - border-right: 0; + border-right: 0; } .finished { - background-color: #CCFFCC; - border-bottom: 1px; + background-color: #CCFFCC; + border-bottom: 1px; } .yui-dt-last .finished { - border-bottom: 0; + border-bottom: 0; } /* @@ -67,57 +67,57 @@ version: 2.8.1 */ .yui-pb-bar { - background-color: blue; + background-color: blue; } .yui-pb { - border: thin solid #808080 + border: thin solid #808080 } .hippo-root .yui-pb { - background-color: transparent; - border: solid #808080; - border-width: 1px 0; + background-color: transparent; + border: solid #808080; + border-width: 1px 0; } .hippo-root .yui-pb-rtl, .hippo-root .yui-pb-ltr { - background-image: url(back-h.png); - background-repeat: repeat-x; + background-image: url(back-h.png); + background-repeat: repeat-x; } .hippo-root .yui-pb-ttb, .hippo-root .yui-pb-btt { - background-image: url(back-v.png); - background-repeat: repeat-y; + background-image: url(back-v.png); + background-repeat: repeat-y; } .hippo-root .yui-pb-bar { - background-color: transparent; + background-color: transparent; } .hippo-root .yui-pb-ltr .yui-pb-bar, .hippo-root .yui-pb-rtl .yui-pb-bar { - background-image: url(bar-h.png); - background-repeat: repeat-x; + background-image: url(bar-h.png); + background-repeat: repeat-x; } .hippo-root .yui-pb-ttb .yui-pb-bar, .hippo-root .yui-pb-btt .yui-pb-bar { - background-image: url(bar-v.png); - background-repeat: repeat-y; + background-image: url(bar-v.png); + background-repeat: repeat-y; } .hippo-root .yui-pb-mask { - border: solid #808080; - border-width: 0 1px; - margin: 0 -1px; + border: solid #808080; + border-width: 0 1px; + margin: 0 -1px; } .hippo-root .yui-pb-caption { - color: #000; - text-align: center; - margin: 0 auto; + color: #000; + text-align: center; + margin: 0 auto; } .hippo-root .yui-pb-range { - color: #a6a6a6; + color: #a6a6a6; } /* Datatable skin */ @@ -131,197 +131,197 @@ /* basic skin styles */ .hippo-root .yui-dt table { - margin: 0; - padding: 0; - font-family: arial; - font-size: inherit; - border-collapse: separate; - *border-collapse: collapse; - border-spacing: 0; /* since ie6 and ie7 behave differently */ - border: 1px solid #7F7F7F; + margin: 0; + padding: 0; + font-family: arial; + font-size: inherit; + border-collapse: separate; + *border-collapse: collapse; + border-spacing: 0; /* since ie6 and ie7 behave differently */ + border: 1px solid #7F7F7F; } .hippo-root .yui-dt thead { - border-spacing: 0; + border-spacing: 0; } /* for safari bug */ .hippo-root .yui-dt caption { - color: #000000; - font-size: 85%; - font-weight: normal; - font-style: italic; - line-height: 1; - padding: 1em 0pt; - text-align: center; + color: #000000; + font-size: 85%; + font-weight: normal; + font-style: italic; + line-height: 1; + padding: 1em 0pt; + text-align: center; } .hippo-root .yui-dt th { - background: #D8D8DA url(sprite.png) repeat-x 0 0; /* header gradient */ + background: #D8D8DA url(sprite.png) repeat-x 0 0; /* header gradient */ } .hippo-root .yui-dt th, .hippo-root .yui-dt th a { - font-weight: normal; - text-decoration: none; - color: #000; /* header text */ - vertical-align: bottom; + font-weight: normal; + text-decoration: none; + color: #000; /* header text */ + vertical-align: bottom; } .hippo-root .yui-dt th { - margin: 0; - padding: 0; - border: none; - border-right: 1px solid #CBCBCB; /* inner column border */ + margin: 0; + padding: 0; + border: none; + border-right: 1px solid #CBCBCB; /* inner column border */ } .hippo-root .yui-dt tr.yui-dt-first td { - border-top: 1px solid #7F7F7F; /* tbody top border */ + border-top: 1px solid #7F7F7F; /* tbody top border */ } .hippo-root .yui-dt th .yui-dt-liner { - white-space: nowrap; + white-space: nowrap; } .hippo-root .yui-dt-liner { - margin: 0; - padding: 0; - padding: 3px 10px 3px 10px; /* cell padding */ + margin: 0; + padding: 0; + padding: 3px 10px 3px 10px; /* cell padding */ } .hippo-root .yui-dt-coltarget { - width: 5px; - background-color: red; + width: 5px; + background-color: red; } .hippo-root .yui-dt td { - margin: 0; - padding: 0; - border: none; - border-right: 1px solid #CBCBCB; /* inner column border */ - text-align: left; + margin: 0; + padding: 0; + border: none; + border-right: 1px solid #CBCBCB; /* inner column border */ + text-align: left; } .hippo-root .yui-dt-list td { - border-right: none; /* disable inner column border in list mode */ + border-right: none; /* disable inner column border in list mode */ } .hippo-root .yui-dt-resizer { - width: 6px; + width: 6px; } /* mask */ .hippo-root .yui-dt-mask { - background-color: #000; - opacity: .25; - filter: alpha(opacity = 25); /* Set opacity in IE */ + background-color: #000; + opacity: .25; + filter: alpha(opacity = 25); /* Set opacity in IE */ } /* messaging */ .hippo-root .yui-dt-message { - background-color: #FFF; + background-color: #FFF; } /* scrolling */ .hippo-root .yui-dt-scrollable { - overflow: hidden; + overflow: hidden; } .hippo-root .yui-dt-scrollable table { - border: none; + border: none; } .hippo-root .yui-dt-scrollable .yui-dt-hd { - border-left: 1px solid #7F7F7F; - border-top: 1px solid #7F7F7F; - border-right: 1px solid #7F7F7F; + border-left: 1px solid #7F7F7F; + border-top: 1px solid #7F7F7F; + border-right: 1px solid #7F7F7F; } .hippo-root .yui-dt-scrollable .yui-dt-bd { - border-left: 1px solid #7F7F7F; - border-bottom: 1px solid #7F7F7F; - border-right: 1px solid #7F7F7F; - background-color: #FFF; + border-left: 1px solid #7F7F7F; + border-bottom: 1px solid #7F7F7F; + border-right: 1px solid #7F7F7F; + background-color: #FFF; } .hippo-root .yui-dt-scrollable .yui-dt-data tr.yui-dt-last td { - border-bottom: 1px solid #7F7F7F; + border-bottom: 1px solid #7F7F7F; } /* sortable columns */ .hippo-root th.yui-dt-asc, .hippo-root th.yui-dt-desc { - background: url(sprite.png) repeat-x 0 -100px; /* sorted header gradient */ + background: url(sprite.png) repeat-x 0 -100px; /* sorted header gradient */ } .hippo-root th.yui-dt-sortable .yui-dt-label { - margin-right: 10px; + margin-right: 10px; } .hippo-root th.yui-dt-asc .yui-dt-liner { - background: url(dt-arrow-up.png) no-repeat right; /* sorted header gradient */ + background: url(dt-arrow-up.png) no-repeat right; /* sorted header gradient */ } .hippo-root th.yui-dt-desc .yui-dt-liner { - background: url(dt-arrow-dn.png) no-repeat right; /* sorted header gradient */ + background: url(dt-arrow-dn.png) no-repeat right; /* sorted header gradient */ } /* editing */ tbody .yui-dt-editable { - cursor: pointer; + cursor: pointer; } .yui-dt-editor { - text-align: left; - background-color: #F2F2F2; - border: 1px solid #808080; - padding: 6px; + text-align: left; + background-color: #F2F2F2; + border: 1px solid #808080; + padding: 6px; } .yui-dt-editor label { - padding-left: 4px; - padding-right: 6px; + padding-left: 4px; + padding-right: 6px; } .yui-dt-editor .yui-dt-button { - padding-top: 6px; - text-align: right; + padding-top: 6px; + text-align: right; } .yui-dt-editor .yui-dt-button button { - background: url(sprite.png) repeat-x 0 0; - border: 1px solid #999; - width: 4em; - height: 1.8em; - margin-left: 6px; + background: url(sprite.png) repeat-x 0 0; + border: 1px solid #999; + width: 4em; + height: 1.8em; + margin-left: 6px; } .yui-dt-editor .yui-dt-button button.yui-dt-default { - background: url(sprite.png) repeat-x 0 -1400px; - background-color: #5584E0; - border: 1px solid #304369; - color: #FFF + background: url(sprite.png) repeat-x 0 -1400px; + background-color: #5584E0; + border: 1px solid #304369; + color: #FFF } .yui-dt-editor .yui-dt-button button:hover { - background: url(sprite.png) repeat-x 0 -1300px; - color: #000; + background: url(sprite.png) repeat-x 0 -1300px; + color: #000; } .yui-dt-editor .yui-dt-button button:active { - background: url(sprite.png) repeat-x 0 -1700px; - color: #000; + background: url(sprite.png) repeat-x 0 -1700px; + color: #000; } /* striping */ .hippo-root tr.yui-dt-even { - background-color: #FFF; + background-color: #FFF; } /* white */ .hippo-root tr.yui-dt-odd { - background-color: #EDF5FF; + background-color: #EDF5FF; } /* light blue */ @@ -334,24 +334,24 @@ /* disable striping in list mode */ .hippo-root .yui-dt-list tr.yui-dt-even { - background-color: #FFF; + background-color: #FFF; } /* white */ .hippo-root .yui-dt-list tr.yui-dt-odd { - background-color: #FFF; + background-color: #FFF; } /* white */ .hippo-root .yui-dt-list tr.yui-dt-even td.yui-dt-asc, .hippo-root .yui-dt-list tr.yui-dt-even td.yui-dt-desc { - background-color: #EDF5FF; + background-color: #EDF5FF; } /* light blue sorted */ .hippo-root .yui-dt-list tr.yui-dt-odd td.yui-dt-asc, .hippo-root .yui-dt-list tr.yui-dt-odd td.yui-dt-desc { - background-color: #EDF5FF; + background-color: #EDF5FF; } /* light blue sorted */ @@ -359,7 +359,7 @@ /* highlighting */ .hippo-root th.yui-dt-highlighted, .hippo-root th.yui-dt-highlighted a { - background-color: #B2D2FF; /* med blue hover */ + background-color: #B2D2FF; /* med blue hover */ } .hippo-root tr.yui-dt-highlighted, @@ -367,14 +367,14 @@ .hippo-root tr.yui-dt-highlighted td.yui-dt-desc, .hippo-root tr.yui-dt-even td.yui-dt-highlighted, .hippo-root tr.yui-dt-odd td.yui-dt-highlighted { - cursor: pointer; - background-color: #B2D2FF; /* med blue hover */ + cursor: pointer; + background-color: #B2D2FF; /* med blue hover */ } /* enable highlighting in list mode */ .hippo-root .yui-dt-list th.yui-dt-highlighted, .hippo-root .yui-dt-list th.yui-dt-highlighted a { - background-color: #B2D2FF; /* med blue hover */ + background-color: #B2D2FF; /* med blue hover */ } .hippo-root .yui-dt-list tr.yui-dt-highlighted, @@ -382,79 +382,145 @@ .hippo-root .yui-dt-list tr.yui-dt-highlighted td.yui-dt-desc, .hippo-root .yui-dt-list tr.yui-dt-even td.yui-dt-highlighted, .hippo-root .yui-dt-list tr.yui-dt-odd td.yui-dt-highlighted { - cursor: pointer; - background-color: #B2D2FF; /* med blue hover */ + cursor: pointer; + background-color: #B2D2FF; /* med blue hover */ } /* selection */ .hippo-root th.yui-dt-selected, .hippo-root th.yui-dt-selected a { - background-color: #446CD7; /* bright blue selected cell */ + background-color: #446CD7; /* bright blue selected cell */ } .hippo-root tr.yui-dt-selected td, .hippo-root tr.yui-dt-selected td.yui-dt-asc, .hippo-root tr.yui-dt-selected td.yui-dt-desc { - background-color: #426FD9; /* bright blue selected row */ - color: #FFF; + background-color: #426FD9; /* bright blue selected row */ + color: #FFF; } .hippo-root tr.yui-dt-even td.yui-dt-selected, .hippo-root tr.yui-dt-odd td.yui-dt-selected { - background-color: #446CD7; /* bright blue selected cell */ - color: #FFF; + background-color: #446CD7; /* bright blue selected cell */ + color: #FFF; } /* enable selection in list mode */ .hippo-root .yui-dt-list th.yui-dt-selected, .hippo-root .yui-dt-list th.yui-dt-selected a { - background-color: #446CD7; /* bright blue selected cell */ + background-color: #446CD7; /* bright blue selected cell */ } .hippo-root .yui-dt-list tr.yui-dt-selected td, .hippo-root .yui-dt-list tr.yui-dt-selected td.yui-dt-asc, .hippo-root .yui-dt-list tr.yui-dt-selected td.yui-dt-desc { - background-color: #426FD9; /* bright blue selected row */ - color: #FFF; + background-color: #426FD9; /* bright blue selected row */ + color: #FFF; } .hippo-root .yui-dt-list tr.yui-dt-even td.yui-dt-selected, .hippo-root .yui-dt-list tr.yui-dt-odd td.yui-dt-selected { - background-color: #446CD7; /* bright blue selected cell */ - color: #FFF; + background-color: #446CD7; /* bright blue selected cell */ + color: #FFF; } /* pagination */ .hippo-root .yui-dt-paginator { - display: block; - margin: 6px 0; - white-space: nowrap; + display: block; + margin: 6px 0; + white-space: nowrap; } .hippo-root .yui-dt-paginator .yui-dt-first, .hippo-root .yui-dt-paginator .yui-dt-last, .hippo-root .yui-dt-paginator .yui-dt-selected { - padding: 2px 6px; + padding: 2px 6px; } .hippo-root .yui-dt-paginator a.yui-dt-first, .hippo-root .yui-dt-paginator a.yui-dt-last { - text-decoration: none; + text-decoration: none; } .hippo-root .yui-dt-paginator .yui-dt-previous, .hippo-root .yui-dt-paginator .yui-dt-next { - display: none; + display: none; } .hippo-root a.yui-dt-page { - border: 1px solid #CBCBCB; - padding: 2px 6px; - text-decoration: none; - background-color: #fff + border: 1px solid #CBCBCB; + padding: 2px 6px; + text-decoration: none; + background-color: #fff } .hippo-root .yui-dt-selected { - border: 1px solid #fff; - background-color: #fff; + border: 1px solid #fff; + background-color: #fff; } + +.hippo-root .yui-dialog * { + margin: 0; + border: 0; + font-weight: normal; + font-style: normal; + font-size: 100%; + font-family: inherit; + text-align: left; +} + +.hippo-root .yui-simple-dialog .container-close { + top: 3px; +} + +.hippo-root .yui-skin-sam .yui-panel { + border-width: 1px; +} + +.hippo-root .yui-skin-sam .yui-dialog .ft span.default button { + color: #000; + background-color: #e3e3e3; + border: 1px solid #000; +} + +.hippo-root .yui-skin-sam .yui-dialog .ft span.default button:hover { + background-color: #fff; + border: 1px solid #000; +} + +.hippo-root .yui-skin-sam .yui-dialog strong { + font-weight: bold; +} + +.hippo-root .yui-dialog * { + margin: 0; + border: 0; + font-weight: normal; + font-style: normal; + font-size: 100%; + font-family: inherit; + text-align: left; +} + +.hippo-root .yui-simple-dialog .container-close { + top: 3px; +} + +.hippo-root .yui-skin-sam .yui-panel { + border-width: 1px; +} + +.hippo-root .yui-skin-sam .yui-dialog .ft span.default button { + color: #000; + background-color: #e3e3e3; + border: 1px solid #000; +} + +.hippo-root .yui-skin-sam .yui-dialog .ft span.default button:hover { + background-color: #fff; + border: 1px solid #000; +} + +.hippo-root .yui-skin-sam .yui-dialog strong { + font-weight: bold; +} \ No newline at end of file Index: gallery/frontend/src/main/java/org/hippoecm/frontend/plugins/gallery/GalleryWorkflowPlugin.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- gallery/frontend/src/main/java/org/hippoecm/frontend/plugins/gallery/GalleryWorkflowPlugin.java (revision 34000) +++ gallery/frontend/src/main/java/org/hippoecm/frontend/plugins/gallery/GalleryWorkflowPlugin.java (revision ) @@ -48,7 +48,6 @@ import org.hippoecm.frontend.plugins.gallery.model.GalleryProcessor; import org.hippoecm.frontend.plugins.yui.upload.MultiFileUploadDialog; import org.hippoecm.frontend.service.IBrowseService; -import org.hippoecm.frontend.service.IEditorManager; import org.hippoecm.frontend.service.ISettingsService; import org.hippoecm.frontend.session.UserSession; import org.hippoecm.frontend.translation.ILocaleProvider; @@ -78,8 +77,8 @@ public class UploadDialog extends MultiFileUploadDialog { private static final long serialVersionUID = 1L; - public UploadDialog(IPluginConfig pluginConfig) { - super(pluginConfig); + public UploadDialog(IPluginContext context, IPluginConfig config) { + super(context, config); } public IModel getTitle() { @@ -265,7 +264,7 @@ typeComponent = new Label("type", "default").setVisible(false); } - UploadDialog dialog = new UploadDialog(getPluginConfig()); + UploadDialog dialog = new UploadDialog(getPluginContext(), getPluginConfig()); dialog.add(typeComponent); return dialog; } Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidget.html IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidget.html (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidget.html (revision ) @@ -23,6 +23,7 @@
[ Upload widget placeholder]
+
[ Feedback ]
\ No newline at end of file Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/DefaultUploadValidationService.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/DefaultUploadValidationService.java (revision ) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/DefaultUploadValidationService.java (revision ) @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2012 Hippo. + * + * Licensed 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.hippoecm.frontend.plugins.yui.upload.validation; + +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.wicket.markup.html.form.upload.FileUpload; +import org.apache.wicket.util.lang.Bytes; +import org.apache.wicket.util.value.IValueMap; +import org.hippoecm.frontend.validation.IValidationResult; +import org.hippoecm.frontend.validation.ValidationException; +import org.hippoecm.frontend.validation.ValidationResult; +import org.hippoecm.frontend.validation.Violation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DefaultUploadValidationService implements IUploadValidationService { + + private static final Logger log = LoggerFactory.getLogger(DefaultUploadValidationService.class); + + public static final String DEFAULT_MAX_SIZE = "2mb"; + public static final String[] DEFAULT_EXTENSIONS_ALLOWED = new String[0]; + + public static final String EXTENSIONS_ALLOWED = "extensions.allowed"; + public static final String MAX_FILE_SIZE = "max.file.size"; + + private ValidationResult result; + + private List allowedExtensions = new LinkedList(); + private Bytes maxFileSize; + + public DefaultUploadValidationService(IValueMap params) { + + final String[] fileExtensions; + if (params.containsKey(EXTENSIONS_ALLOWED)) { + fileExtensions = params.getStringArray(EXTENSIONS_ALLOWED); + } else { + fileExtensions = getDefaultExtensionsAllowed(); + } + + for (String extension : fileExtensions) { + int pIndex = extension.indexOf("*."); + if (pIndex > -1) { + extension = extension.substring(pIndex + 2); + } + allowedExtensions.add(extension.toLowerCase()); + } + + maxFileSize = Bytes.valueOf(params.getString(MAX_FILE_SIZE, getDefaultMaxFileSize())); + } + + @Override + public void validate(final FileUpload upload) throws ValidationException { + result = new ValidationResult(); + + validateExtension(upload); + validateMaxFileSize(upload); + } + + @Override + public void validate() throws ValidationException { + throw new UnsupportedOperationException("Use validate(FileUpload upload) instead"); + } + + @Override + public IValidationResult getValidationResult() { + return result; + } + + @Override + public void addViolation(final String key, final Object... params) { + result.getViolations().add(new Violation(null, key, params)); + } + + + private void validateExtension(FileUpload upload) { + if (allowedExtensions.size() > 0) { + String fileName = upload.getClientFileName(); + int lastPeriod = fileName.lastIndexOf('.'); + if (lastPeriod == -1) { + if (allowedExtensions.size() > 0) { + addViolation("upload.validation.extension.not.found", + fileName, StringUtils.join(allowedExtensions.iterator(), ", ")); + + if (log.isDebugEnabled()) { + log.debug("File '{}' has no extension. Allowed extensions are {}.", + new Object[]{fileName, StringUtils.join(allowedExtensions.iterator(), ", ")}); + } + } + } else { + String extension = fileName.substring(lastPeriod + 1).toLowerCase(); + if (!allowedExtensions.contains(extension)) { + addViolation("upload.validation.extension.not.allowed", + fileName, extension, StringUtils.join(allowedExtensions.iterator(), ", ")); + + if (log.isDebugEnabled()) { + log.debug("File '{}' has extension {} which is not allowed. Allowed extensions are {}.", + new Object[]{fileName, extension, StringUtils.join(allowedExtensions.iterator(), ", ")}); + } + } + } + } + } + + private void validateMaxFileSize(final FileUpload upload) { + Bytes fileSize = Bytes.bytes(upload.getSize()); + + if (maxFileSize.compareTo(fileSize) == -1) { + addViolation("upload.validation.filesize", + upload.getClientFileName(), fileSize.toString(), maxFileSize.toString()); + + if (log.isDebugEnabled()) { + log.debug("File '{}' has size {} which is too big. The maximum size allowed is {}", new Object[]{upload.getClientFileName(), fileSize.toString(), maxFileSize.toString()}); + } + } + } + + @Override + public String[] getAllowedExtensions() { + return allowedExtensions.toArray(new String[allowedExtensions.size()]); + } + + @Override + public Bytes getMaxFileSize() { + return maxFileSize; + } + + protected String getDefaultMaxFileSize() { + return DEFAULT_MAX_SIZE; + } + + protected String[] getDefaultExtensionsAllowed() { + return DEFAULT_EXTENSIONS_ALLOWED; + } + +} Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/ajax/AjaxMultiFileUploadBehavior.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/ajax/AjaxMultiFileUploadBehavior.java (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/ajax/AjaxMultiFileUploadBehavior.java (revision ) @@ -15,6 +15,10 @@ */ package org.hippoecm.frontend.plugins.yui.upload.ajax; +import java.io.Serializable; + +import javax.servlet.http.HttpServletRequest; + import org.apache.wicket.RequestCycle; import org.apache.wicket.ResourceReference; import org.apache.wicket.ajax.AjaxRequestTarget; @@ -25,10 +29,8 @@ import org.hippoecm.frontend.plugins.yui.HippoNamespace; import org.hippoecm.frontend.plugins.yui.header.IYuiContext; import org.hippoecm.frontend.plugins.yui.header.templates.DynamicTextTemplate; +import org.onehippo.yui.YahooNamespace; -import javax.servlet.http.HttpServletRequest; -import java.io.Serializable; - public class AjaxMultiFileUploadBehavior extends AbstractYuiAjaxBehavior { @SuppressWarnings("unused") private static final String SVN_ID = "$Id: AjaxMultiFileUploadBehavior.java 27301 2011-03-11 09:13:27Z mchatzidakis $"; @@ -47,11 +49,14 @@ @Override public Serializable getSettings() { + String key = settings.isAllowMultipleFiles() ? "select.files.link" : settings.isUploadAfterSelect() ? + "upload.file.link" : "select.file.link"; settings.addTranslation("select.files.link", - settings.isAllowMultipleFiles() ? new StringResourceModel("select.files.link", getComponent(), null).getString() : - settings.isUploadAfterSelect() ? new StringResourceModel("upload.file.link", getComponent(), null).getString() : - new StringResourceModel("select.file.link", getComponent(), null).getString() - ); + new StringResourceModel(key, getComponent(), null).getString()); + settings.addTranslation("filesize.too.large", + new StringResourceModel("filesize.too.large", getComponent(), null).getString()); + settings.addTranslation("error.dialog.header", + new StringResourceModel("error.dialog.header", getComponent(), null).getString()); return settings; } @@ -61,6 +66,9 @@ @Override public void addHeaderContribution(IYuiContext context) { context.addCssReference(new ResourceReference(AjaxMultiFileUploadBehavior.class, "res/skin.css")); + context.addCssReference(new ResourceReference(YahooNamespace.class, + YahooNamespace.NS.getPath() + "container/assets/skins/sam/container.css")); + context.addModule(HippoNamespace.NS, "upload"); context.addTemplate(template); context.addOnWinLoad("YAHOO.hippo.Upload.render()"); @@ -74,11 +82,12 @@ ajaxRequestTarget.appendJavascript("YAHOO.hippo.Upload.restoreScrollPosition(" + r.getParameter( "scrollPosY") + ");"); } - onFinish(ajaxRequestTarget); + boolean hasError = r.getParameter("hasError") != null && r.getParameter("hasError").equals("true"); + onFinish(ajaxRequestTarget, hasError); } } - protected void onFinish(AjaxRequestTarget ajaxRequestTarget) { + protected void onFinish(AjaxRequestTarget ajaxRequestTarget, boolean hasError) { } } Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/multifile/MultiFileUploadFieldCustomized.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/multifile/MultiFileUploadFieldCustomized.js (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/multifile/MultiFileUploadFieldCustomized.js (revision ) @@ -76,15 +76,15 @@ /** * Add a new file input element */ - this.addElement = function(element) { + this.addElement = function (element) { // Make sure it's a file input element if (element.tagName.toLowerCase() == 'input' && element.type.toLowerCase() == 'file') { - if(this.submitAfterSelect && this.form == null) { + if (this.submitAfterSelect && this.form == null) { var p = element.parentNode; - while(p != document.body) { + while (p != document.body) { - if(p.tagName.toLowerCase() == 'form') { + if (p.tagName.toLowerCase() == 'form') { this.form = p; break; } @@ -99,7 +99,7 @@ element.multi_selector = this; // What to do when a file is selected - element.onchange = function() { + element.onchange = function () { if (element.multi_selector.submitAfterSelect) { element.multi_selector.form.submit(); @@ -143,9 +143,9 @@ /** * Add a new row to the list of files */ - this.addListRow = function(element) { + this.addListRow = function (element) { - if(this.count >= 8) { + if (this.count >= 8) { //turn container element into a scrollable unit with set height. this.list_container.className = 'wicket-mfu-row-container-scrollable'; } @@ -167,7 +167,7 @@ new_row.element = element; // Delete function - new_row_button.onclick = function() { + new_row_button.onclick = function () { // Remove element from form this.parentNode.element.parentNode.removeChild(this.parentNode.element); @@ -178,7 +178,7 @@ // Decrement counter this.parentNode.element.multi_selector.count--; - if(this.parentNode.element.multi_selector.count < 8) { + if (this.parentNode.element.multi_selector.count < 8) { this.parentNode.element.multi_selector.list_container.className = 'wicket-mfu-row-container'; } @@ -204,7 +204,7 @@ this.appendToList(new_row); }; - this.appendToList = function(row) { + this.appendToList = function (row) { if (this.list_container == null) { this.list_container = document.createElement("div"); this.list_container.className = 'wicket-mfu-row-container'; @@ -213,7 +213,7 @@ this.list_container.appendChild(row); }; - this.parseFilename = function(filename) { + this.parseFilename = function (filename) { if (YAHOO.env.ua.ie) { //IE passes in the whole filepath, strip it. var s = filename.split('\\'); \ No newline at end of file Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/multifile/MultiFileUploadComponent.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/multifile/MultiFileUploadComponent.java (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/multifile/MultiFileUploadComponent.java (revision ) @@ -39,14 +39,14 @@ private final MultiFileUploadField uploadField; - public MultiFileUploadComponent(String id, int max) { + public MultiFileUploadComponent(String id, int maxFiles) { super(id); setOutputMarkupId(true); Form form = new Form("uploadform"); add(form); - form.add(uploadField = new MultiFileUploadField("input", new Model(new LinkedList()), max) { + form.add(uploadField = new MultiFileUploadField("input", new Model(new LinkedList()), maxFiles) { private static final long serialVersionUID = 1L; @Override Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/ajax/AjaxMultiFileUploadSettings.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/ajax/AjaxMultiFileUploadSettings.java (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/ajax/AjaxMultiFileUploadSettings.java (revision ) @@ -15,11 +15,11 @@ */ package org.hippoecm.frontend.plugins.yui.upload.ajax; -import org.hippoecm.frontend.plugins.yui.AjaxSettings; - import java.util.HashMap; import java.util.Map; +import org.hippoecm.frontend.plugins.yui.AjaxSettings; + public class AjaxMultiFileUploadSettings extends AjaxSettings { final static String SVN_ID = "$Id: AjaxMultiFileUploadSettings.java 27301 2011-03-11 09:13:27Z mchatzidakis $"; @@ -62,6 +62,11 @@ //Specify the height of the button private String buttonHeight; + //Maximum file size allowed, this is check on the client before upload is started + private long maxFileSize; + + private boolean clientSideValidation; + public boolean isAllowMultipleFiles() { return allowMultipleFiles; } @@ -170,4 +175,19 @@ this.buttonHeight = buttonHeight; } + public long getMaxFileSize() { + return maxFileSize; + } + + public void setMaxFileSize(final long maxFileSize) { + this.maxFileSize = maxFileSize; + } + + public boolean isClientSideValidation() { + return clientSideValidation; + } + + public void setClientSideValidation(final boolean clientSideValidation) { + this.clientSideValidation = clientSideValidation; + } } Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidget.css IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidget.css (revision ) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidget.css (revision ) @@ -0,0 +1,17 @@ +.hippo-dialog-container .upload-feedback-panel ul.feedbackPanel { + margin-top: 5px; + border: 1px solid #999; + background-color: #ffc; + color: red; +} + +.hippo-editor-field .upload-feedback-panel ul.feedbackPanel { + margin-top: 15px; + border: 1px solid #999; + background-color: #ffc; + color: red; +} + +.hippo-editor-field .upload-feedback-panel ul.feedbackPanel li { + float: none; +} \ No newline at end of file Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/ajax/AjaxMultiFileUploadComponent.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/ajax/AjaxMultiFileUploadComponent.java (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/ajax/AjaxMultiFileUploadComponent.java (revision ) @@ -15,6 +15,10 @@ */ package org.hippoecm.frontend.plugins.yui.upload.ajax; +import java.util.LinkedList; + +import javax.servlet.http.HttpServletRequest; + import org.apache.wicket.Application; import org.apache.wicket.IRequestTarget; import org.apache.wicket.RequestCycle; @@ -33,14 +37,10 @@ import org.apache.wicket.util.upload.DiskFileItemFactory; import org.apache.wicket.util.upload.FileItem; import org.apache.wicket.util.upload.FileUploadException; - import org.hippoecm.frontend.plugins.yui.upload.MagicMimeTypeFileItem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.servlet.http.HttpServletRequest; -import java.util.LinkedList; - public abstract class AjaxMultiFileUploadComponent extends Panel { @SuppressWarnings("unused") private static final String SVN_ID = "$Id: AjaxMultiFileUploadComponent.java 25041 2010-11-15 08:43:29Z abogaart $"; @@ -66,8 +66,7 @@ }); for (FileItem fi : multipartServletWebRequest.getFiles().values()) { onFileUpload(new FileUpload(fi)); - fi.delete(); - onUploadSuccess(); + //fi.delete(); } setResponse("success"); } catch (FileUploadException e) { @@ -117,13 +116,13 @@ add(new AjaxMultiFileUploadBehavior(settings) { @Override - protected void onFinish(AjaxRequestTarget ajaxRequestTarget) { - AjaxMultiFileUploadComponent.this.onFinish(ajaxRequestTarget); + protected void onFinish(AjaxRequestTarget ajaxRequestTarget, boolean hasError) { + AjaxMultiFileUploadComponent.this.onFinish(ajaxRequestTarget, hasError); } }); add(uploadBehavior = new UploadBehavior()); - + this.settings = settings; } @@ -137,8 +136,6 @@ protected abstract void onFileUpload(FileUpload fileUpload); - protected abstract void onFinish(AjaxRequestTarget target); - - protected abstract void onUploadSuccess(); + protected abstract void onFinish(AjaxRequestTarget target, boolean hasError); } Index: gallery/repository/src/main/resources/gallery-workflows.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- gallery/repository/src/main/resources/gallery-workflows.xml (revision 34000) +++ gallery/repository/src/main/resources/gallery-workflows.xml (revision ) @@ -61,12 +61,15 @@ service.translator.search - + + service.gallery.image.validation + + 25 Index: gallery/frontend/src/main/java/org/hippoecm/frontend/plugins/gallery/editor/ImageUploadPlugin.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- gallery/frontend/src/main/java/org/hippoecm/frontend/plugins/gallery/editor/ImageUploadPlugin.java (revision 34000) +++ gallery/frontend/src/main/java/org/hippoecm/frontend/plugins/gallery/editor/ImageUploadPlugin.java (revision ) @@ -17,18 +17,15 @@ import java.io.IOException; import java.util.Calendar; +import java.util.List; import javax.jcr.Node; import javax.jcr.RepositoryException; -import org.apache.commons.lang.StringUtils; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.upload.FileUpload; -import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.util.value.IValueMap; -import org.apache.wicket.util.value.ValueMap; import org.hippoecm.frontend.behaviors.EventStoppingBehavior; -import org.hippoecm.frontend.dialog.ExceptionDialog; import org.hippoecm.frontend.model.JcrNodeModel; import org.hippoecm.frontend.plugin.IPluginContext; import org.hippoecm.frontend.plugin.config.IPluginConfig; @@ -37,14 +34,14 @@ import org.hippoecm.frontend.plugins.gallery.model.GalleryProcessor; import org.hippoecm.frontend.plugins.yui.upload.FileUploadWidget; import org.hippoecm.frontend.plugins.yui.upload.FileUploadWidgetSettings; +import org.hippoecm.frontend.plugins.yui.upload.validation.IUploadValidationService; import org.hippoecm.frontend.service.render.RenderPlugin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Plugin for uploading images. The plugin can be configured by setting configuration options found in the - * {@link FileUploadWidgetSettings}. - * + * Plugin for uploading images. The plugin can be configured by setting configuration options found in the {@link + * FileUploadWidgetSettings}. */ public class ImageUploadPlugin extends RenderPlugin { @SuppressWarnings("unused") @@ -54,24 +51,20 @@ static final Logger log = LoggerFactory.getLogger(ImageUploadPlugin.class); + private FileUploadWidgetSettings uploadSettings; private IValueMap types; public ImageUploadPlugin(final IPluginContext context, IPluginConfig config) { super(context, config); - // if the types config is not set, all extensions are allowed - String typesConfig = config.getString("types"); - if (typesConfig != null) { - types = new ValueMap(typesConfig); - } + uploadSettings = new FileUploadWidgetSettings(getPluginConfig()); - + FileUploadForm form = new FileUploadForm("form"); add(form); String mode = config.getString("mode", "edit"); form.setVisible("edit".equals(mode)); add(new EventStoppingBehavior("onclick")); - } private class FileUploadForm extends Form { @@ -82,14 +75,21 @@ public FileUploadForm(String name) { super(name); - FileUploadWidgetSettings settings = new FileUploadWidgetSettings(getPluginConfig()); + String serviceId = getPluginConfig().getString(IUploadValidationService.VALIDATE_ID, "service.validation.image"); + IUploadValidationService validator = getPluginContext().getService(serviceId, IUploadValidationService.class); - add(widget = new FileUploadWidget("multifile", settings) { + add(widget = new FileUploadWidget("multifile", uploadSettings, validator) { @Override protected void onFileUpload(FileUpload fileUpload) { handleUpload(fileUpload); } + @Override + protected void renderErrors(final List errors) { + for(String error : errors) { + error(error); + } + } }); } @@ -100,46 +100,30 @@ } - private void handleUpload(FileUpload upload) { String fileName = upload.getClientFileName(); String mimeType = upload.getContentType(); - String extension = fileName.substring(fileName.lastIndexOf('.') + 1); - - // check if obligatory types/file extensions are set and matched - if (types != null && types.getString(extension.toLowerCase()) == null) { - String extensions = StringUtils.join(types.keySet().toArray(), ", "); - getDialogService().show( - new ExceptionDialog(new StringResourceModel("unrecognized", ImageUploadPlugin.this, null, - new Object[]{extension, extensions}).getString()) { - public IValueMap getProperties() { - return SMALL; - } - - }); - log.warn("Unrecognised file type"); - } else { - JcrNodeModel nodeModel = (JcrNodeModel) ImageUploadPlugin.this.getDefaultModel(); - Node node = nodeModel.getNode(); - try { - GalleryProcessor processor = getPluginContext().getService(getPluginConfig().getString("gallery.processor.id", - "gallery.processor.service"), GalleryProcessor.class); - if (processor == null) { - processor = new DefaultGalleryProcessor(); - } - processor.initGalleryResource(node, upload.getInputStream(), mimeType, fileName, Calendar.getInstance()); - processor.validateResource(node, fileName); - } catch (RepositoryException ex) { - error(ex); - log.error(ex.getMessage()); - } catch (IOException ex) { - // FIXME: report back to user - log.error(ex.getMessage()); - } catch (GalleryException ex) { - error(ex); - log.error(ex.getMessage()); - } - } + JcrNodeModel nodeModel = (JcrNodeModel) ImageUploadPlugin.this.getDefaultModel(); + Node node = nodeModel.getNode(); + try { + GalleryProcessor processor = getPluginContext().getService(getPluginConfig().getString("gallery.processor.id", + "gallery.processor.service"), GalleryProcessor.class); + if (processor == null) { + processor = new DefaultGalleryProcessor(); + } + processor.initGalleryResource(node, upload.getInputStream(), mimeType, fileName, Calendar.getInstance()); + processor.validateResource(node, fileName); + } catch (RepositoryException ex) { + error(ex); + log.error(ex.getMessage()); + } catch (IOException ex) { + // FIXME: report back to user + log.error(ex.getMessage()); + } catch (GalleryException ex) { + error(ex); + log.error(ex.getMessage()); + } + } - } + } Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/IUploadValidationService.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/IUploadValidationService.java (revision ) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/IUploadValidationService.java (revision ) @@ -0,0 +1,36 @@ +/* + * Copyright 2012 Hippo. + * + * Licensed 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. + * + * 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.hippoecm.frontend.plugins.yui.upload.validation; + +import org.apache.wicket.markup.html.form.upload.FileUpload; +import org.apache.wicket.util.lang.Bytes; +import org.hippoecm.frontend.validation.IValidationService; +import org.hippoecm.frontend.validation.ValidationException; + +public interface IUploadValidationService extends IValidationService { + final static String SVN_ID = "$Id$"; + + String DEFAULT_MAX_FILE_SIZE = "1mb"; + + void validate(FileUpload upload) throws ValidationException; + + String[] getAllowedExtensions(); + + Bytes getMaxFileSize(); + + void addViolation(final String key, final Object... params); +} Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/ImageUploadValidationService.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/ImageUploadValidationService.java (revision ) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/validation/ImageUploadValidationService.java (revision ) @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2012 Hippo. + * + * Licensed 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.hippoecm.frontend.plugins.yui.upload.validation; + +import java.io.IOException; + +import org.apache.wicket.markup.html.form.upload.FileUpload; +import org.apache.wicket.util.value.IValueMap; +import org.hippoecm.frontend.plugins.yui.upload.util.ImageInfo; +import org.hippoecm.frontend.validation.ValidationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ImageUploadValidationService extends DefaultUploadValidationService { + + private static final Logger log = LoggerFactory.getLogger(ImageUploadValidationService.class); + + public static final int DEFAULT_MAX_WIDTH = 1280; + public static final int DEFAULT_MAX_HEIGHT = 1024; + public static final String DEFAULT_MAX_SIZE = "500kb"; + public static final String[] DEFAULT_EXTENSIONS_ALLOWED = new String[]{"*.jpg", "*.jpeg", "*.gif", "*.png"}; + + int maxWidth; + int maxHeight; + + public ImageUploadValidationService(IValueMap params) { + super(params); + + maxWidth = params.getInt("max.width", getDefaultMaxWidth()); + maxHeight = params.getInt("max.height", getDefaultMaxHeight()); + } + + @Override + public void validate(final FileUpload upload) throws ValidationException { + super.validate(upload); + validateSizes(upload); + } + + private void validateSizes(final FileUpload upload) { + String fileName = upload.getClientFileName(); + ImageInfo info; + try { + info = new ImageInfo(upload.getInputStream()); + } catch (IOException e) { + addViolation("upload.validation.ioerror", fileName); + + log.error("Error processing upload", e); + return; + } + + //check image dimensions + int imgWidth = info.getWidth(), imgHeight = info.getHeight(); + boolean tooWide = maxWidth > 0 && imgWidth > maxWidth; + boolean tooHigh = maxHeight > 0 && imgHeight > maxHeight; + + if (tooWide && tooHigh) { + addViolation("upload.validation.image.width-height", fileName, imgWidth, imgHeight, maxWidth, maxHeight); + if (log.isDebugEnabled()) { + log.debug("Image '{}' resolution is too high ({},{}). The max allowed width is ({}, {})", + new Object[]{fileName, imgWidth, imgHeight, maxWidth, maxHeight}); + + } + } else if (tooWide) { + addViolation("upload.validation.image.width", fileName, imgWidth, maxWidth); + if (log.isDebugEnabled()) { + log.debug("Image '{}' is {} pixels wide while the max allowed width is {}", + new Object[]{fileName, imgWidth, maxWidth}); + } + } else if (tooHigh) { + addViolation("upload.validation.image.height", fileName, imgWidth, maxWidth); + if (log.isDebugEnabled()) { + log.debug("Image '{}' is {} pixels high while the max allowed height is {}", + new Object[]{fileName, imgHeight, maxHeight}); + } + } + } + + protected int getDefaultMaxWidth() { + return DEFAULT_MAX_WIDTH; + } + + protected int getDefaultMaxHeight() { + return DEFAULT_MAX_HEIGHT; + } + + @Override + protected String[] getDefaultExtensionsAllowed() { + return DEFAULT_EXTENSIONS_ALLOWED; + } + + @Override + protected String getDefaultMaxFileSize() { + return DEFAULT_MAX_SIZE; + } + +} Index: gallery/repository/src/main/resources/image-validator.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- gallery/repository/src/main/resources/image-validator.xml (revision ) +++ gallery/repository/src/main/resources/image-validator.xml (revision ) @@ -0,0 +1,30 @@ + + + + + frontend:plugin + + + org.hippoecm.frontend.plugins.yui.upload.validation.ImageUploadValidationPlugin + + + service.gallery.image.validation + + + service.gallery.image.validation + + Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/multifile/MultiFileUploadComponent.html IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/multifile/MultiFileUploadComponent.html (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/multifile/MultiFileUploadComponent.html (revision ) @@ -22,9 +22,9 @@ -
-
[ input ]
-
+
+
[ input ]
+
\ No newline at end of file Index: api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidget.properties IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidget.properties (revision 34000) +++ api/src/main/java/org/hippoecm/frontend/plugins/yui/upload/FileUploadWidget.properties (revision ) @@ -1,2 +1,15 @@ extension.not.allowed:The uploaded file has an extension that is not allowed. Allowed extensions are: {0} -extension.not.found:No extension found on uploaded file {0}. Allowed extensions are: {1} \ No newline at end of file +extension.not.found:No extension found on uploaded file {0}. Allowed extensions are: {1} + +filesize.too.large:File {0} is too large ({1}), the maximum file size allowed is {2}. +error.dialog.header: Warning + +upload.validation.extension.not.allowed:The uploaded file {0} has extension {1} which is not allowed. Allowed extensions are: {2} +upload.validation.extension.not.found:No extension found on uploaded file {0}. Allowed extensions are: {1} +upload.validation.filesize:File {0} is too large, the maximum allowed file size is {2}. + +upload.validation.image.width: Image {0} is too wide, max allowed horizontal pixels is {2} +upload.validation.image.height: Image {0} is too high, max allowed vertical pixels is {2} +upload.validation.image.width-height: Resolution of {0} is too high ({1}, {2}), max allowed resolution is ({3}, {4}) + +upload.validation.ioerror: An error occurred while reading/writing file from disk, please try again. \ No newline at end of file