You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2021/11/26 14:31:12 UTC

[openmeetings] 02/02: [OPENMEETINGS-2687] upload is blocked on start, progress is improved

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

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

commit 6f361458665a7ccfa53201a98c25acb5331f57d8
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Fri Nov 26 21:30:56 2021 +0700

    [OPENMEETINGS-2687] upload is blocked on start, progress is improved
---
 .../upload/UploadForm.html}                        |  30 +++---
 .../openmeetings/web/common/upload/UploadForm.java |  75 +++++++++++++
 .../openmeetings/web/common/upload/upload.js       | 119 +++++++++++++++++++++
 .../sidebar/RoomFileUploadResourceReference.java   |   8 +-
 .../openmeetings/web/room/sidebar/RoomSidebar.java |   2 +-
 .../web/room/sidebar/RoomUploadForm.html           |  35 ++++++
 .../{UploadDialog.java => RoomUploadForm.java}     |  47 ++------
 .../web/room/sidebar/UploadDialog.html             |  27 +----
 .../web/room/sidebar/UploadDialog.java             |  57 +---------
 .../openmeetings/web/room/sidebar/room-upload.js   | 119 ++-------------------
 10 files changed, 270 insertions(+), 249 deletions(-)

diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/upload/UploadForm.html
similarity index 50%
copy from openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.html
copy to openmeetings-web/src/main/java/org/apache/openmeetings/web/common/upload/UploadForm.html
index 2333d66..1ceb323 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/upload/UploadForm.html
@@ -20,32 +20,26 @@
 -->
 <!DOCTYPE html>
 <html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-9.xsd">
-<wicket:extend>
-	<form wicket:id="form" id="room-upload-form" method="post" enctype="multipart/form-data">
+<wicket:panel>
+	<form wicket:id="form" id="omws-upload-form" method="post" enctype="multipart/form-data"
+			wicket:message="data-upload-lbl:593, data-max-upload-lbl:1491">
 		<div class="mb-4"><wicket:message key="594"/></div>
 		<div class="fileinput fileinput-new d-inline m-0" data-provides="fileinput">
 			<div class="fileinput-filename d-block"></div>
 			<span class="btn btn-file btn-outline-primary">
 				<span><wicket:message key="596"/></span>
-				<input type="file" multiple="multiple" id="room-upload-file" name="room-upload-file"/>
+				<input type="file" multiple="multiple" id="omws-upload-file" name="omws-upload-file"/>
 			</span>
 		</div>
+		<input wicket:id="sid" id="omws-upload-sid" name="omws-upload-sid" type="hidden"/>
 		<div class="error"></div>
-		<div class="mt-3">
-			<input id="room-upload-to-wb" name="room-upload-to-wb" type="checkbox" value="true"/>&nbsp;<label for="room-upload-to-wb"><wicket:message key="1312"/></label>
-			<div class="me-3 d-none" id="room-upload-clean-block">
-				<input id="room-upload-clean" name="room-upload-clean" type="checkbox" value="true"/>&nbsp;<label for="room-upload-clean"><wicket:message key="62"/></label>
-			</div>
-		</div>
-		<input wicket:id="sid" name="room-upload-sid" type="hidden"/>
-		<input wicket:id="lastSelectedId" name="room-upload-last-selected-id" type="hidden" value=""/>
-		<input wicket:id="lastSelectedRoom" name="room-upload-last-selected-room" type="hidden" value=""/>
-		<input wicket:id="lastSelectedOwner" name="room-upload-last-selected-owner" type="hidden" value=""/>
-		<input wicket:id="lastSelectedGroup" name="room-upload-last-selected-group" type="hidden" value=""/>
+		<wicket:child/>
 	</form>
-	<div class="progress d-none">
-		<div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
+	<div class="progress-block d-none">
+		<h5 class="progress-title" wicket:message="data-converting-lbl:upload.dlg.convert.title, data-upload-lbl:702"></h5>
+		<div class="progress">
+			<div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
+		</div>
 	</div>
-	<div id="progress"></div>
-</wicket:extend>
+</wicket:panel>
 </html>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/upload/UploadForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/upload/UploadForm.java
new file mode 100644
index 0000000..7982a7d
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/upload/UploadForm.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License") +  you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openmeetings.web.common.upload;
+
+import static org.apache.openmeetings.util.OpenmeetingsVariables.getMaxUploadSize;
+
+import org.apache.openmeetings.web.app.WebSession;
+import org.apache.openmeetings.web.common.MainPanel;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.JavaScriptHeaderItem;
+import org.apache.wicket.markup.head.PriorityHeaderItem;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.request.resource.JavaScriptResourceReference;
+import org.apache.wicket.util.lang.Bytes;
+
+public class UploadForm extends Panel {
+	private static final long serialVersionUID = 1L;
+	private final String action;
+	protected final WebMarkupContainer form = new WebMarkupContainer("form");
+
+	public UploadForm(String id, String action) {
+		super(id);
+		this.action = action;
+		setRenderBodyOnly(true);
+	}
+
+	@Override
+	protected void onInitialize() {
+		final MainPanel mainPanel = findParent(MainPanel.class);
+
+		add(form.add(AttributeModifier.append("data-max-size", getMaxUploadSize()))
+				.add(AttributeModifier.append("data-max-size-lbl", Bytes.bytes(getMaxUploadSize()).toString(WebSession.get().getLocale())))
+				.add(AttributeModifier.append("action", action))
+				.setOutputMarkupId(true)
+				.setOutputMarkupPlaceholderTag(true));
+		form.add(new WebMarkupContainer("sid")
+				.add(AttributeModifier.append("value", mainPanel.getClient().getSid()))
+				.setOutputMarkupId(true));
+		super.onInitialize();
+	}
+
+	public void show(IPartialPageRequestHandler handler) {
+		show(handler, "null");
+	}
+
+	public void show(IPartialPageRequestHandler handler, String extraFunc) {
+		handler.add(form.setVisible(true));
+		handler.appendJavaScript("Upload.bindUpload(" + extraFunc +");");
+	}
+
+	@Override
+	public void renderHead(IHeaderResponse response) {
+		super.renderHead(response);
+		response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(new JavaScriptResourceReference(UploadForm.class, "upload.js"))));
+	}
+}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/upload/upload.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/upload/upload.js
new file mode 100644
index 0000000..1bdd7de
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/upload/upload.js
@@ -0,0 +1,119 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
+var Upload = (function() {
+	let progress, progressBar, progressTitle, curUid;
+
+	function _setProgress(prg) {
+		const progressP = prg + '%';
+		progressBar.css('width', progressP)
+			.attr('aria-valuenow', prg)
+			.text(progressP);
+	}
+	function _onWsMessage(_, msg) {
+		try {
+			if (msg instanceof Blob) {
+				return; //ping
+			}
+			const m = JSON.parse(msg);
+			if (m && 'omws-upload' === m.type && curUid === m.uuid) {
+				switch (m.status) {
+					case 'ERROR':
+						OmUtil.error(m.message);
+						break;
+					case 'PROGRESS':
+						_setProgress(m.progress)
+						if (m.progress === 100) {
+							$('#omws-upload-form').parents('.modal').modal('hide');
+							_cleanup();
+						}
+						break;
+					default:
+						//no-op
+				}
+			}
+		} catch (err) {
+			OmUtil.error(err);
+		}
+	}
+	function _cleanup() {
+		Wicket.Event.unsubscribe('/websocket/message', _onWsMessage);
+	}
+	function _bindUpload(extaBindFunc) {
+		const form = $('#omws-upload-form')
+				, fi = form.find('.fileinput')
+		progress = form.parent().find('.progress-block');
+		progressTitle = progress.find('.progress-title');
+		progressBar = progress.find('.progress-bar');
+		_cleanup();
+		form.show();
+		progress.addClass('d-none');
+		let uploadBtn = $('#omws-upload-btn');
+		if (uploadBtn.length === 0) {
+			uploadBtn = $('<button id="omws-upload-btn" class="btn btn-outline-primary"></button>')
+				.text(form.data('upload-lbl'));
+			form.parents('.modal-content').find('.modal-footer').prepend(uploadBtn);
+			uploadBtn.click(function() {
+				const cform = $('#omws-upload-form');
+				$.ajax({
+					url: cform.attr('action')
+					, type: 'POST'
+					, data: new FormData($('#omws-upload-form')[0])
+					, processData: false
+					, contentType: false
+					, xhr: function () {
+						const xhr = new XMLHttpRequest();
+						xhr.upload.addEventListener('progress', evt => _setProgress((100 * evt.loaded / evt.total).toFixed(2)), false);
+						xhr.upload.addEventListener('loadstart', () => {
+							uploadBtn.attr('disabled', 'disabled');
+							cform.hide();
+							progressTitle.text(progressTitle.data('upload-lbl'));
+							progress.removeClass('d-none');
+						}, false);
+						return xhr;
+					}
+				}).done(function(data) {
+					curUid = data.uuid;
+					progressTitle.text(progressTitle.data('converting-lbl'));
+					_setProgress(0);
+					Wicket.Event.subscribe('/websocket/message', _onWsMessage);
+				}).fail(function(e) {
+					OmUtil.error(e.message);
+				});
+			});
+		}
+		uploadBtn.attr('disabled', 'disabled');
+		fi.off().on('change.bs.fileinput', function(event) {
+			event.stopPropagation();
+			const th = $(this)
+				, fInput = th.find('input[type=file]')
+				, fn = th.find('.fileinput-filename')
+				, files = fInput[0].files
+				, errors = form.find('.error');
+			errors.html('');
+			let	valid = files !== undefined && files.length > 0;
+			if (valid) {
+				const size = Array.from(files).map(f => f.size).reduce((a, b) => a + b, 0);
+				valid = size < +form.data('max-size');
+				if (!valid) {
+					errors.html($('<div class="alert alert-danger" role="alert"></div>').text(form.data('max-upload-lbl') + ' ' + form.data('max-size-lbl')));
+				}
+			}
+			if (valid) {
+				fn.text(Array.from(files).map(f => f.name).join(', '));
+				uploadBtn.removeAttr('disabled');
+			} else {
+				fn.text('');
+				uploadBtn.attr('disabled', 'disabled');
+				$('#omws-upload-file').val('');
+			}
+			fInput.attr('title', fn.text());
+			return false;
+		});
+		if ('function' === typeof(extaBindFunc)) {
+			extaBindFunc();
+		}
+	}
+
+	return {
+		bindUpload: _bindUpload
+	};
+})();
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomFileUploadResourceReference.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomFileUploadResourceReference.java
index 3a8af9f..21e7305 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomFileUploadResourceReference.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomFileUploadResourceReference.java
@@ -67,13 +67,13 @@ import com.github.openjson.JSONObject;
 public class RoomFileUploadResourceReference extends ResourceReference {
 	private static final long serialVersionUID = 1L;
 	private static final Logger log = LoggerFactory.getLogger(RoomFileUploadResourceReference.class);
-	private static final String PARAM_FILE_NAME = "room-upload-file";
+	private static final String PARAM_FILE_NAME = "omws-upload-file";
+	private static final String PARAM_SID_NAME = "omws-upload-sid";
 	private static final String PARAM_TO_WB_NAME = "room-upload-to-wb";
 	private static final String PARAM_CLEAN_NAME = "room-upload-clean";
-	private static final String PARAM_SID_NAME = "room-upload-sid";
 	public static final String PARAM_LAST_SELECTED_ID = "room-upload-last-selected-id";
 	public static final String PARAM_LAST_SELECTED_ROOM = "room-upload-last-selected-room";
-	public static final String PARAM_LAST_SELECTED_OWNER = "room-upload-last-selected-pwner";
+	public static final String PARAM_LAST_SELECTED_OWNER = "room-upload-last-selected-owner";
 	public static final String PARAM_LAST_SELECTED_GROUP = "room-upload-last-selected-group";
 	private enum Status {
 		SUCCESS
@@ -225,7 +225,7 @@ public class RoomFileUploadResourceReference extends ResourceReference {
 	private JSONObject getBaseMessage(String uuid) {
 		return new JSONObject()
 				.put("uuid", uuid)
-				.put("type", "room-upload");
+				.put("type", "omws-upload");
 	}
 	private void sendError(Client c, String uuid, String msg) {
 		WebSocketHelper.sendClient(c, getBaseMessage(uuid)
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
index b9cc76d..79101d4 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
@@ -87,7 +87,7 @@ public class RoomSidebar extends Panel {
 		add(fileTab.setVisible(!room.isInterview()), roomFiles.setVisible(!room.isInterview()));
 
 		add(addFolder, settings);
-		add(upload = new UploadDialog("upload", room, roomFiles));
+		add(upload = new UploadDialog("upload", roomFiles));
 		updateShowFiles(null);
 		add(activities = new ActivitiesPanel("activities", room));
 	}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomUploadForm.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomUploadForm.html
new file mode 100644
index 0000000..3eaa6e5
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomUploadForm.html
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+
+-->
+<!DOCTYPE html>
+<html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-9.xsd">
+<wicket:extend>
+	<div class="mt-3">
+		<input id="room-upload-to-wb" name="room-upload-to-wb" type="checkbox" value="true"/>&nbsp;<label for="room-upload-to-wb"><wicket:message key="1312"/></label>
+		<div class="me-3 d-none" id="room-upload-clean-block">
+			<input id="room-upload-clean" name="room-upload-clean" type="checkbox" value="true"/>&nbsp;<label for="room-upload-clean"><wicket:message key="62"/></label>
+		</div>
+	</div>
+	<input wicket:id="lastSelectedId" name="room-upload-last-selected-id" type="hidden" value=""/>
+	<input wicket:id="lastSelectedRoom" name="room-upload-last-selected-room" type="hidden" value=""/>
+	<input wicket:id="lastSelectedOwner" name="room-upload-last-selected-owner" type="hidden" value=""/>
+	<input wicket:id="lastSelectedGroup" name="room-upload-last-selected-group" type="hidden" value=""/>
+</wicket:extend>
+</html>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomUploadForm.java
similarity index 64%
copy from openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.java
copy to openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomUploadForm.java
index c1b6510..18dfae8 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomUploadForm.java
@@ -18,72 +18,47 @@
  */
 package org.apache.openmeetings.web.room.sidebar;
 
-import static org.apache.openmeetings.util.OpenmeetingsVariables.getMaxUploadSize;
 import static org.apache.openmeetings.web.room.sidebar.RoomFileUploadResourceReference.PARAM_LAST_SELECTED_GROUP;
 import static org.apache.openmeetings.web.room.sidebar.RoomFileUploadResourceReference.PARAM_LAST_SELECTED_ID;
 import static org.apache.openmeetings.web.room.sidebar.RoomFileUploadResourceReference.PARAM_LAST_SELECTED_OWNER;
 import static org.apache.openmeetings.web.room.sidebar.RoomFileUploadResourceReference.PARAM_LAST_SELECTED_ROOM;
 
 import org.apache.openmeetings.db.entity.file.BaseFileItem;
-import org.apache.openmeetings.web.app.WebSession;
-import org.apache.openmeetings.web.common.OmModalCloseButton;
-import org.apache.openmeetings.web.room.RoomPanel;
-import org.apache.openmeetings.web.util.upload.BootstrapFileUploadBehavior;
+import org.apache.openmeetings.web.common.upload.UploadForm;
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.JavaScriptHeaderItem;
 import org.apache.wicket.markup.head.PriorityHeaderItem;
 import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.resource.JavaScriptResourceReference;
-import org.apache.wicket.util.lang.Bytes;
 
-import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
-
-public class UploadDialog extends Modal<String> {
+public class RoomUploadForm extends UploadForm {
 	private static final long serialVersionUID = 1L;
-	private final WebMarkupContainer form = new WebMarkupContainer("form");
-	private final RoomFilePanel roomFiles;
-	private final RoomPanel room;
 	private final WebMarkupContainer lastSelectedId = new WebMarkupContainer("lastSelectedId");
 	private final WebMarkupContainer lastSelectedRoom = new WebMarkupContainer("lastSelectedRoom"); // required for "fake" root
 	private final WebMarkupContainer lastSelectedOwner = new WebMarkupContainer("lastSelectedOwner"); // required for "fake" root
 	private final WebMarkupContainer lastSelectedGroup = new WebMarkupContainer("lastSelectedGroup"); // required for "fake" root
+	private final RoomFilePanel roomFiles;
 
-	public UploadDialog(String id, RoomPanel room, RoomFilePanel roomFiles) {
-		super(id);
+	public RoomUploadForm(String id, RoomFilePanel roomFiles) {
+		super(id, "" + RequestCycle.get().urlFor(new RoomFileUploadResourceReference(), new PageParameters()));
 		this.roomFiles = roomFiles;
-		this.room = room;
 	}
 
 	@Override
 	protected void onInitialize() {
-		header(new ResourceModel("upload.dlg.choose.title"));
-		setCloseOnEscapeKey(false);
-		setBackdrop(Backdrop.STATIC);
-
-		add(form.add(AttributeModifier.append("data-max-size", getMaxUploadSize()))
-				.add(AttributeModifier.append("data-max-size-lbl", Bytes.bytes(getMaxUploadSize()).toString(WebSession.get().getLocale())))
-				.add(AttributeModifier.append("data-upload-lbl", getString("593")))
-				.add(AttributeModifier.append("data-max-upload-lbl", getString("1491")))
-				.add(AttributeModifier.append("action", "" + urlFor(new RoomFileUploadResourceReference(), new PageParameters())))
-				.setOutputMarkupId(true)
-				.setOutputMarkupPlaceholderTag(true));
-		form.add(new WebMarkupContainer("sid").add(AttributeModifier.append("value", room.getClient().getSid())).setMarkupId("room-upload-sid").setOutputMarkupId(true));
+		super.onInitialize();
 		form.add(lastSelectedId.setMarkupId(PARAM_LAST_SELECTED_ID).setOutputMarkupId(true));
 		form.add(lastSelectedRoom.setMarkupId(PARAM_LAST_SELECTED_ROOM).setOutputMarkupId(true));
 		form.add(lastSelectedOwner.setMarkupId(PARAM_LAST_SELECTED_OWNER).setOutputMarkupId(true));
 		form.add(lastSelectedGroup.setMarkupId(PARAM_LAST_SELECTED_GROUP).setOutputMarkupId(true));
-		add(BootstrapFileUploadBehavior.INSTANCE);
-		addButton(OmModalCloseButton.of("85"));
-
-		super.onInitialize();
 	}
 
 	@Override
-	public Modal<String> show(IPartialPageRequestHandler handler) {
+	public void show(IPartialPageRequestHandler handler) {
 		BaseFileItem last = roomFiles.getLastSelected();
 		if (last.getId() == null) {
 			lastSelectedRoom.add(AttributeModifier.replace("value", last.getRoomId()));
@@ -92,14 +67,12 @@ public class UploadDialog extends Modal<String> {
 		} else {
 			lastSelectedId.add(AttributeModifier.replace("value", last.getId()));
 		}
-		handler.add(form.setVisible(true));
-		handler.appendJavaScript("Upload.bindUpload();");
-		return super.show(handler);
+		super.show(handler, "roomUploadExtaBindFunc");
 	}
 
 	@Override
 	public void renderHead(IHeaderResponse response) {
 		super.renderHead(response);
-		response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(new JavaScriptResourceReference(UploadDialog.class, "upload.js"))));
+		response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(new JavaScriptResourceReference(RoomUploadForm.class, "room-upload.js"))));
 	}
 }
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.html
index 2333d66..3ed94d4 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.html
@@ -21,31 +21,6 @@
 <!DOCTYPE html>
 <html xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-9.xsd">
 <wicket:extend>
-	<form wicket:id="form" id="room-upload-form" method="post" enctype="multipart/form-data">
-		<div class="mb-4"><wicket:message key="594"/></div>
-		<div class="fileinput fileinput-new d-inline m-0" data-provides="fileinput">
-			<div class="fileinput-filename d-block"></div>
-			<span class="btn btn-file btn-outline-primary">
-				<span><wicket:message key="596"/></span>
-				<input type="file" multiple="multiple" id="room-upload-file" name="room-upload-file"/>
-			</span>
-		</div>
-		<div class="error"></div>
-		<div class="mt-3">
-			<input id="room-upload-to-wb" name="room-upload-to-wb" type="checkbox" value="true"/>&nbsp;<label for="room-upload-to-wb"><wicket:message key="1312"/></label>
-			<div class="me-3 d-none" id="room-upload-clean-block">
-				<input id="room-upload-clean" name="room-upload-clean" type="checkbox" value="true"/>&nbsp;<label for="room-upload-clean"><wicket:message key="62"/></label>
-			</div>
-		</div>
-		<input wicket:id="sid" name="room-upload-sid" type="hidden"/>
-		<input wicket:id="lastSelectedId" name="room-upload-last-selected-id" type="hidden" value=""/>
-		<input wicket:id="lastSelectedRoom" name="room-upload-last-selected-room" type="hidden" value=""/>
-		<input wicket:id="lastSelectedOwner" name="room-upload-last-selected-owner" type="hidden" value=""/>
-		<input wicket:id="lastSelectedGroup" name="room-upload-last-selected-group" type="hidden" value=""/>
-	</form>
-	<div class="progress d-none">
-		<div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
-	</div>
-	<div id="progress"></div>
+	<div wicket:id="wsupload"></div>
 </wicket:extend>
 </html>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.java
index c1b6510..2024e8d 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/UploadDialog.java
@@ -18,44 +18,21 @@
  */
 package org.apache.openmeetings.web.room.sidebar;
 
-import static org.apache.openmeetings.util.OpenmeetingsVariables.getMaxUploadSize;
-import static org.apache.openmeetings.web.room.sidebar.RoomFileUploadResourceReference.PARAM_LAST_SELECTED_GROUP;
-import static org.apache.openmeetings.web.room.sidebar.RoomFileUploadResourceReference.PARAM_LAST_SELECTED_ID;
-import static org.apache.openmeetings.web.room.sidebar.RoomFileUploadResourceReference.PARAM_LAST_SELECTED_OWNER;
-import static org.apache.openmeetings.web.room.sidebar.RoomFileUploadResourceReference.PARAM_LAST_SELECTED_ROOM;
-
-import org.apache.openmeetings.db.entity.file.BaseFileItem;
-import org.apache.openmeetings.web.app.WebSession;
 import org.apache.openmeetings.web.common.OmModalCloseButton;
-import org.apache.openmeetings.web.room.RoomPanel;
 import org.apache.openmeetings.web.util.upload.BootstrapFileUploadBehavior;
-import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
-import org.apache.wicket.markup.head.IHeaderResponse;
-import org.apache.wicket.markup.head.JavaScriptHeaderItem;
-import org.apache.wicket.markup.head.PriorityHeaderItem;
-import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.model.ResourceModel;
-import org.apache.wicket.request.mapper.parameter.PageParameters;
-import org.apache.wicket.request.resource.JavaScriptResourceReference;
-import org.apache.wicket.util.lang.Bytes;
 
 import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
 
 public class UploadDialog extends Modal<String> {
 	private static final long serialVersionUID = 1L;
-	private final WebMarkupContainer form = new WebMarkupContainer("form");
+	private RoomUploadForm wsUpload;
 	private final RoomFilePanel roomFiles;
-	private final RoomPanel room;
-	private final WebMarkupContainer lastSelectedId = new WebMarkupContainer("lastSelectedId");
-	private final WebMarkupContainer lastSelectedRoom = new WebMarkupContainer("lastSelectedRoom"); // required for "fake" root
-	private final WebMarkupContainer lastSelectedOwner = new WebMarkupContainer("lastSelectedOwner"); // required for "fake" root
-	private final WebMarkupContainer lastSelectedGroup = new WebMarkupContainer("lastSelectedGroup"); // required for "fake" root
 
-	public UploadDialog(String id, RoomPanel room, RoomFilePanel roomFiles) {
+	public UploadDialog(String id, RoomFilePanel roomFiles) {
 		super(id);
 		this.roomFiles = roomFiles;
-		this.room = room;
 	}
 
 	@Override
@@ -64,18 +41,7 @@ public class UploadDialog extends Modal<String> {
 		setCloseOnEscapeKey(false);
 		setBackdrop(Backdrop.STATIC);
 
-		add(form.add(AttributeModifier.append("data-max-size", getMaxUploadSize()))
-				.add(AttributeModifier.append("data-max-size-lbl", Bytes.bytes(getMaxUploadSize()).toString(WebSession.get().getLocale())))
-				.add(AttributeModifier.append("data-upload-lbl", getString("593")))
-				.add(AttributeModifier.append("data-max-upload-lbl", getString("1491")))
-				.add(AttributeModifier.append("action", "" + urlFor(new RoomFileUploadResourceReference(), new PageParameters())))
-				.setOutputMarkupId(true)
-				.setOutputMarkupPlaceholderTag(true));
-		form.add(new WebMarkupContainer("sid").add(AttributeModifier.append("value", room.getClient().getSid())).setMarkupId("room-upload-sid").setOutputMarkupId(true));
-		form.add(lastSelectedId.setMarkupId(PARAM_LAST_SELECTED_ID).setOutputMarkupId(true));
-		form.add(lastSelectedRoom.setMarkupId(PARAM_LAST_SELECTED_ROOM).setOutputMarkupId(true));
-		form.add(lastSelectedOwner.setMarkupId(PARAM_LAST_SELECTED_OWNER).setOutputMarkupId(true));
-		form.add(lastSelectedGroup.setMarkupId(PARAM_LAST_SELECTED_GROUP).setOutputMarkupId(true));
+		add(wsUpload = new RoomUploadForm("wsupload", roomFiles));
 		add(BootstrapFileUploadBehavior.INSTANCE);
 		addButton(OmModalCloseButton.of("85"));
 
@@ -84,22 +50,7 @@ public class UploadDialog extends Modal<String> {
 
 	@Override
 	public Modal<String> show(IPartialPageRequestHandler handler) {
-		BaseFileItem last = roomFiles.getLastSelected();
-		if (last.getId() == null) {
-			lastSelectedRoom.add(AttributeModifier.replace("value", last.getRoomId()));
-			lastSelectedOwner.add(AttributeModifier.replace("value", last.getOwnerId()));
-			lastSelectedGroup.add(AttributeModifier.replace("value", last.getGroupId()));
-		} else {
-			lastSelectedId.add(AttributeModifier.replace("value", last.getId()));
-		}
-		handler.add(form.setVisible(true));
-		handler.appendJavaScript("Upload.bindUpload();");
+		wsUpload.show(handler);
 		return super.show(handler);
 	}
-
-	@Override
-	public void renderHead(IHeaderResponse response) {
-		super.renderHead(response);
-		response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(new JavaScriptResourceReference(UploadDialog.class, "upload.js"))));
-	}
 }
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/room-upload.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/room-upload.js
index 5c40961..e108410 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/room-upload.js
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/room-upload.js
@@ -1,112 +1,11 @@
 /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
-var Upload = (function() {
-	let progress, progressBar, curUid;
-
-	function _onWsMessage(jqEvent, msg) {
-		try {
-			if (msg instanceof Blob) {
-				return; //ping
-			}
-			const m = JSON.parse(msg);
-			if (m && 'room-upload' === m.type && curUid === m.uuid) {
-				switch (m.status) {
-					case 'ERROR':
-						OmUtil.error(m.message);
-						break;
-					case 'PROGRESS': {
-							const progressP = m.progress + '%';
-							progressBar.css('width', progressP).attr('aria-valuenow', m.progress).text(progressP);
-							if (m.progress === 100) {
-								$('#room-upload-form').parents('.modal').modal('hide');
-								_cleanup();
-							}
-						}
-						break;
-					default:
-						//no-op
-				}
-			}
-		} catch (err) {
-			OmUtil.error(err);
+function roomUploadExtaBindFunc() {
+	$('#room-upload-to-wb').off().click(function() {
+		const clnBlock = $('#room-upload-clean-block');
+		if ($(this).prop('checked')) {
+			clnBlock.removeClass('d-none');
+		} else {
+			clnBlock.addClass('d-none');
 		}
-	}
-	function _cleanup() {
-		Wicket.Event.unsubscribe('/websocket/message', _onWsMessage);
-	}
-	function _bindUpload() {
-		const form = $('#room-upload-form')
-				, fi = form.find('.fileinput')
-		progress = form.parent().find('.progress');
-		progressBar = form.parent().find('.progress-bar');
-		_cleanup();
-		form.show();
-		progress.addClass('d-none');
-		let uploadBtn = $('#room-upload-btn');
-		if (uploadBtn.length === 0) {
-			uploadBtn = $('<button id="room-upload-btn" class="btn btn-outline-primary"></button>')
-				.text(form.data('upload-lbl'));
-			form.parents('.modal-content').find('.modal-footer').prepend(uploadBtn);
-			uploadBtn.click(function() {
-				const cform = $('#room-upload-form');
-				$.ajax({
-					url: cform.attr('action')
-					, type: 'POST'
-					, data: new FormData($('#room-upload-form')[0])
-					, processData: false
-					, contentType: false
-				}).done(function(data) {
-					curUid = data.uuid;
-					uploadBtn.attr('disabled', 'disabled');
-					cform.hide();
-					progress.removeClass('d-none');
-					Wicket.Event.subscribe('/websocket/message', _onWsMessage);
-				}).fail(function(e) {
-					OmUtil.error(e.message);
-				});
-			});
-		}
-		uploadBtn.attr('disabled', 'disabled');
-		if (!fi.eventAdded) {
-			fi.on('change.bs.fileinput', function(event) {
-				event.stopPropagation();
-				const th = $(this)
-					, fInput = th.find('input[type=file]')
-					, fn = th.find('.fileinput-filename')
-					, files = fInput[0].files
-					, errors = form.find('.error');
-				errors.html('');
-				let	valid = files !== undefined && files.length > 0;
-				if (valid) {
-					const size = Array.from(files).map(f => f.size).reduce((a, b) => a + b, 0);
-					valid = size < +form.data('max-size');
-					if (!valid) {
-						errors.html($('<div class="alert alert-danger" role="alert"></div>').text(form.data('max-upload-lbl') + ' ' + form.data('max-size-lbl')));
-					}
-				}
-				if (valid) {
-					fn.text(Array.from(files).map(f => f.name).join(', '));
-					uploadBtn.removeAttr('disabled');
-				} else {
-					fn.text('');
-					uploadBtn.attr('disabled', 'disabled');
-					$('#room-upload-file').val('');
-				}
-				fInput.attr('title', fn.text());
-				return false;
-			});
-			$('#room-upload-to-wb').click(function() {
-				const clnBlock = $('#room-upload-clean-block');
-				if ($(this).prop('checked')) {
-					clnBlock.removeClass('d-none');
-				} else {
-					clnBlock.addClass('d-none');
-				}
-			});
-			fi.eventAdded = true;
-		}
-	}
-
-	return {
-		bindUpload: _bindUpload
-	};
-})();
+	});
+}