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 2020/02/14 09:19:55 UTC

[openmeetings] branch csp updated: [OPENMEETINGS-2165] upload dialog is converted

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

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


The following commit(s) were added to refs/heads/csp by this push:
     new 3ca1ca6  [OPENMEETINGS-2165] upload dialog is converted
3ca1ca6 is described below

commit 3ca1ca6e7d95e0094286995cd74cd9a295b8f0a8
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Fri Feb 14 16:19:43 2020 +0700

    [OPENMEETINGS-2165] upload dialog is converted
---
 .../apache/openmeetings/backup/BackupExport.java   | 108 ++++++------
 .../apache/openmeetings/backup/BackupImport.java   |  24 ++-
 .../java/org/apache/openmeetings/cli/Admin.java    |   2 +-
 .../openmeetings/web/admin/AdminActionsPanel.java  |   2 +-
 .../openmeetings/web/admin/backup/BackupPanel.html |   5 +-
 .../openmeetings/web/admin/backup/BackupPanel.java | 192 ++++++++++++---------
 .../web/admin/connection/ConnectionsPanel.java     |   8 +-
 .../openmeetings/web/admin/email/EmailForm.java    |   2 +-
 .../web/admin/groups/GroupUsersPanel.java          |   5 +-
 .../openmeetings/web/admin/labels/LangPanel.html   |   1 -
 .../openmeetings/web/admin/labels/LangPanel.java   |   6 +-
 .../openmeetings/web/admin/oauth/OAuthForm.java    |   4 +-
 .../openmeetings/web/admin/rooms/RoomForm.java     |   8 +-
 .../openmeetings/web/common/FormActionsPanel.java  |   2 +-
 .../apache/openmeetings/web/common/MainPanel.java  |   2 +-
 .../web/common/UploadableImagePanel.java           |   3 +-
 .../common/confirmation/ConfirmableAjaxBorder.java |  19 ++
 .../openmeetings/web/room/sidebar/RoomSidebar.java |   2 +-
 .../web/room/sidebar/UploadDialog.html             |  11 +-
 .../web/room/sidebar/UploadDialog.java             | 156 ++++++-----------
 .../web/user/calendar/CalendarDialog.java          |   2 +-
 .../openmeetings/web/user/chat/ChatToolbar.java    |   2 +-
 .../dashboard/admin/AdminCleanupInfoDialog.java    |   2 +-
 .../web/user/profile/InvitationDetails.java        |   2 +-
 .../web/user/profile/MessagesContactsPanel.java    |   6 +-
 .../web/util/CallbackFunctionHelper.java           |  20 ---
 .../apache/openmeetings/web/util/ThreadHelper.java |  48 ++++++
 .../src/main/webapp/css/raw-general.css            |   5 -
 .../org/apache/openmeetings/backup/TestBackup.java |   2 +-
 29 files changed, 342 insertions(+), 309 deletions(-)

diff --git a/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupExport.java b/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupExport.java
index fcc9348..14172ac 100644
--- a/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupExport.java
+++ b/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupExport.java
@@ -162,24 +162,42 @@ public class BackupExport {
 			 */
 			writeList(ser, zos, "version.xml", "version", Arrays.asList(BackupVersion.get()));
 			progressHolder.setProgress(2);
-			exportGroups(zos, progressHolder);
-			exportUsers(zos, progressHolder);
-			exportRoom(zos, progressHolder);
-			exportRoomGroup(zos, progressHolder);
-			exportRoomFile(zos, progressHolder);
-			exportCalendar(zos, progressHolder);
-			exportAppointment(zos, progressHolder);
-			exportMeetingMember(zos, progressHolder);
-			exportLdap(zos, progressHolder, ser);
-			exportOauth(zos, progressHolder);
-			exportPrivateMsg(zos, progressHolder);
-			exportPrivateMsgFolder(zos, progressHolder, ser);
-			exportContacts(zos, progressHolder);
-			exportFile(zos, progressHolder);
-			exportRecording(zos, progressHolder);
-			exportPoll(zos, progressHolder);
-			exportConfig(zos, progressHolder);
-			exportChat(zos, progressHolder);
+			exportGroups(zos);
+			progressHolder.setProgress(5);
+			exportUsers(zos);
+			progressHolder.setProgress(10);
+			exportRoom(zos);
+			progressHolder.setProgress(15);
+			exportRoomGroup(zos);
+			progressHolder.setProgress(17);
+			exportRoomFile(zos);
+			progressHolder.setProgress(17);
+			exportCalendar(zos);
+			progressHolder.setProgress(22);
+			exportAppointment(zos);
+			progressHolder.setProgress(25);
+			exportMeetingMember(zos);
+			progressHolder.setProgress(30);
+			exportLdap(zos, ser);
+			progressHolder.setProgress(35);
+			exportOauth(zos);
+			progressHolder.setProgress(45);
+			exportPrivateMsg(zos);
+			progressHolder.setProgress(50);
+			exportPrivateMsgFolder(zos, ser);
+			progressHolder.setProgress(55);
+			exportContacts(zos);
+			progressHolder.setProgress(60);
+			exportFile(zos);
+			progressHolder.setProgress(65);
+			exportRecording(zos);
+			progressHolder.setProgress(70);
+			exportPoll(zos);
+			progressHolder.setProgress(75);
+			exportConfig(zos);
+			progressHolder.setProgress(80);
+			exportChat(zos);
+			progressHolder.setProgress(85);
 
 			if (includeFiles) {
 				exportFiles(progressHolder, zos);
@@ -229,20 +247,19 @@ public class BackupExport {
 	/*
 	 * ##################### Backup  Groups
 	 */
-	private void exportGroups(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportGroups(ZipOutputStream zos) throws Exception {
 		Registry registry = new Registry();
 		Strategy strategy = new RegistryStrategy(registry);
 		Serializer ser = new Persister(strategy);
 		List<Group> list = groupDao.get(0, Integer.MAX_VALUE);
 		bindDate(registry, list);
 		writeList(ser, zos, "organizations.xml", "organisations", list);
-		progressHolder.setProgress(5);
 	}
 
 	/*
 	 * ##################### Backup Users
 	 */
-	private void exportUsers(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportUsers(ZipOutputStream zos) throws Exception {
 		Registry registry = new Registry();
 		Strategy strategy = new RegistryStrategy(registry);
 		Serializer ser = new Persister(strategy);
@@ -253,13 +270,12 @@ public class BackupExport {
 		bindDate(registry, list);
 
 		writeList(ser, zos, "users.xml", "users", list);
-		progressHolder.setProgress(10);
 	}
 
 	/*
 	 * ##################### Backup Room
 	 */
-	private void exportRoom(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportRoom(ZipOutputStream zos) throws Exception {
 		Registry registry = new Registry();
 		Strategy strategy = new RegistryStrategy(registry);
 		Serializer serializer = new Persister(strategy);
@@ -269,13 +285,12 @@ public class BackupExport {
 		List<Room> list = roomDao.get();
 		bindDate(registry, list);
 		writeList(serializer, zos, "rooms.xml", "rooms", list);
-		progressHolder.setProgress(15);
 	}
 
 	/*
 	 * ##################### Backup Room Groups
 	 */
-	private void exportRoomGroup(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportRoomGroup(ZipOutputStream zos) throws Exception {
 		Registry registry = new Registry();
 		Strategy strategy = new RegistryStrategy(registry);
 		Serializer serializer = new Persister(strategy);
@@ -284,13 +299,12 @@ public class BackupExport {
 		registry.bind(Room.class, RoomConverter.class);
 
 		writeList(serializer, zos, "rooms_organisation.xml", "room_organisations", roomDao.getGroups());
-		progressHolder.setProgress(17);
 	}
 
 	/*
 	 * ##################### Backup Room Files
 	 */
-	private void exportRoomFile(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportRoomFile(ZipOutputStream zos) throws Exception {
 		Registry registry = new Registry();
 		Strategy strategy = new RegistryStrategy(registry);
 		Serializer serializer = new Persister(strategy);
@@ -299,13 +313,12 @@ public class BackupExport {
 		registry.bind(Recording.class, BaseFileItemConverter.class);
 
 		writeList(serializer, zos, "roomFiles.xml", "RoomFiles", roomDao.getFiles());
-		progressHolder.setProgress(17);
 	}
 
 	/*
 	 * ##################### Backup Calendars
 	 */
-	private void exportCalendar(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportCalendar(ZipOutputStream zos) throws Exception {
 		List<OmCalendar> list = calendarDao.get();
 		Registry registry = new Registry();
 		Strategy strategy = new RegistryStrategy(registry);
@@ -314,13 +327,12 @@ public class BackupExport {
 		bindDate(registry, list);
 
 		writeList(serializer, zos, "calendars.xml", "calendars", list);
-		progressHolder.setProgress(22);
 	}
 
 	/*
 	 * ##################### Backup Appointments
 	 */
-	private void exportAppointment(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportAppointment(ZipOutputStream zos) throws Exception {
 		List<Appointment> list = appointmentDao.get();
 		Registry registry = new Registry();
 		Strategy strategy = new RegistryStrategy(registry);
@@ -332,13 +344,12 @@ public class BackupExport {
 		bindDate(registry, list);
 
 		writeList(serializer, zos, "appointements.xml", "appointments", list);
-		progressHolder.setProgress(25);
 	}
 
 	/*
 	 * ##################### Backup Meeting Members
 	 */
-	private void exportMeetingMember(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportMeetingMember(ZipOutputStream zos) throws Exception {
 		Registry registry = new Registry();
 		Strategy strategy = new RegistryStrategy(registry);
 		Serializer serializer = new Persister(strategy);
@@ -348,38 +359,35 @@ public class BackupExport {
 
 		writeList(serializer, zos, "meetingmembers.xml",
 				"meetingmembers", meetingMemberDao.getMeetingMembers());
-		progressHolder.setProgress(30);
 	}
 
 	/*
 	 * ##################### LDAP Configs
 	 */
-	private void exportLdap(ZipOutputStream zos, ProgressHolder progressHolder, Serializer ser) throws Exception {
+	private void exportLdap(ZipOutputStream zos, Serializer ser) throws Exception {
 		List<LdapConfig> ldapList = ldapConfigDao.get();
 		if (!ldapList.isEmpty()) {
 			ldapList.remove(0);
 		}
 		writeList(ser, zos, "ldapconfigs.xml", "ldapconfigs", ldapList);
-		progressHolder.setProgress(35);
 	}
 
 	/*
 	 * ##################### OAuth2 servers
 	 */
-	private void exportOauth(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportOauth(ZipOutputStream zos) throws Exception {
 		Registry registry = new Registry();
 		Strategy strategy = new RegistryStrategy(registry);
 		Serializer serializer = new Persister(strategy);
 		List<OAuthServer> list = auth2Dao.get(0, Integer.MAX_VALUE);
 		bindDate(registry, list);
 		writeList(serializer, zos, "oauth2servers.xml", "oauth2servers", list);
-		progressHolder.setProgress(45);
 	}
 
 	/*
 	 * ##################### Private Messages
 	 */
-	private void exportPrivateMsg(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportPrivateMsg(ZipOutputStream zos) throws Exception {
 		List<PrivateMessage> list = privateMessageDao.get(0, Integer.MAX_VALUE);
 		Registry registry = new Registry();
 		Strategy strategy = new RegistryStrategy(registry);
@@ -389,22 +397,20 @@ public class BackupExport {
 		registry.bind(Room.class, RoomConverter.class);
 		bindDate(registry, list, PrivateMessage::getInserted);
 		writeList(serializer, zos, "privateMessages.xml", "privatemessages", list);
-		progressHolder.setProgress(50);
 	}
 
 	/*
 	 * ##################### Private Message Folders
 	 */
-	private void exportPrivateMsgFolder(ZipOutputStream zos, ProgressHolder progressHolder, Serializer ser) throws Exception {
+	private void exportPrivateMsgFolder(ZipOutputStream zos, Serializer ser) throws Exception {
 		writeList(ser, zos, "privateMessageFolder.xml",
 				"privatemessagefolders", privateMessageFolderDao.get(0, Integer.MAX_VALUE));
-		progressHolder.setProgress(55);
 	}
 
 	/*
 	 * ##################### User Contacts
 	 */
-	private void exportContacts(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportContacts(ZipOutputStream zos) throws Exception {
 		Registry registry = new Registry();
 		Strategy strategy = new RegistryStrategy(registry);
 		Serializer serializer = new Persister(strategy);
@@ -412,13 +418,12 @@ public class BackupExport {
 		registry.bind(User.class, UserConverter.class);
 
 		writeList(serializer, zos, "userContacts.xml", "usercontacts", userContactDao.get());
-		progressHolder.setProgress(60);
 	}
 
 	/*
 	 * ##################### File-Explorer
 	 */
-	private void exportFile(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportFile(ZipOutputStream zos) throws Exception {
 		List<FileItem> list = fileItemDao.get();
 		Registry registry = new Registry();
 		Strategy strategy = new RegistryStrategy(registry);
@@ -426,13 +431,12 @@ public class BackupExport {
 
 		bindDate(registry, list);
 		writeList(serializer, zos, "fileExplorerItems.xml", "fileExplorerItems", list);
-		progressHolder.setProgress(65);
 	}
 
 	/*
 	 * ##################### Recordings
 	 */
-	private void exportRecording(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportRecording(ZipOutputStream zos) throws Exception {
 		List<Recording> list = recordingDao.get();
 		Registry registry = new Registry();
 		Strategy strategy = new RegistryStrategy(registry);
@@ -440,13 +444,12 @@ public class BackupExport {
 
 		bindDate(registry, list);
 		writeList(serializer, zos, "flvRecordings.xml", "flvrecordings", list);
-		progressHolder.setProgress(70);
 	}
 
 	/*
 	 * ##################### Polls
 	 */
-	private void exportPoll(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportPoll(ZipOutputStream zos) throws Exception {
 		List<RoomPoll> list = pollManager.get();
 		Registry registry = new Registry();
 		Strategy strategy = new RegistryStrategy(registry);
@@ -457,24 +460,22 @@ public class BackupExport {
 		registry.bind(RoomPoll.Type.class, PollTypeConverter.class);
 		bindDate(registry, list, RoomPoll::getCreated);
 		writeList(serializer, zos, "roompolls.xml", "roompolls", list);
-		progressHolder.setProgress(75);
 	}
 
 	/*
 	 * ##################### Config
 	 */
-	private void exportConfig(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportConfig(ZipOutputStream zos) throws Exception {
 		List<Configuration> list = configurationDao.get(0, Integer.MAX_VALUE);
 		Serializer serializer = getConfigSerializer(list);
 
 		writeList(serializer, zos, "configs.xml", "configs", list);
-		progressHolder.setProgress(80);
 	}
 
 	/*
 	 * ##################### Chat
 	 */
-	private void exportChat(ZipOutputStream zos, ProgressHolder progressHolder) throws Exception {
+	private void exportChat(ZipOutputStream zos) throws Exception {
 		List<ChatMessage> list = chatDao.get(0, Integer.MAX_VALUE);
 		Registry registry = new Registry();
 		registry.bind(User.class, UserConverter.class);
@@ -484,7 +485,6 @@ public class BackupExport {
 
 		bindDate(registry, list, ChatMessage::getSent);
 		writeList(serializer, zos, "chat_messages.xml", "chat_messages", list);
-		progressHolder.setProgress(85);
 	}
 
 	private static Serializer getConfigSerializer(List<Configuration> list) throws Exception {
diff --git a/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java b/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java
index 3883f85..fe14b5b 100644
--- a/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java
+++ b/openmeetings-install/src/main/java/org/apache/openmeetings/backup/BackupImport.java
@@ -363,7 +363,8 @@ public class BackupImport {
 		return f;
 	}
 
-	public void performImport(InputStream is) throws Exception {
+	public void performImport(InputStream is, ProgressHolder progressHolder) throws Exception {
+		progressHolder.setProgress(0);
 		userMap.clear();
 		groupMap.clear();
 		calendarMap.clear();
@@ -387,30 +388,50 @@ public class BackupImport {
 		registry.bind(Date.class, DateConverter.class);
 
 		BackupVersion ver = getVersion(simpleSerializer, f);
+		progressHolder.setProgress(2);
 		importConfigs(f);
+		progressHolder.setProgress(7);
 		importGroups(f, simpleSerializer);
+		progressHolder.setProgress(12);
 		importLdap(f, simpleSerializer);
+		progressHolder.setProgress(17);
 		importOauth(f, simpleSerializer);
+		progressHolder.setProgress(22);
 		importUsers(f);
+		progressHolder.setProgress(27);
 		importRooms(f);
+		progressHolder.setProgress(32);
 		importRoomGroups(f);
+		progressHolder.setProgress(37);
 		importChat(f);
+		progressHolder.setProgress(42);
 		importCalendars(f);
+		progressHolder.setProgress(47);
 		importAppointments(f);
+		progressHolder.setProgress(52);
 		importMeetingMembers(f);
+		progressHolder.setProgress(57);
 		importRecordings(f);
+		progressHolder.setProgress(62);
 		importPrivateMsgFolders(f, simpleSerializer);
+		progressHolder.setProgress(67);
 		importContacts(f);
+		progressHolder.setProgress(72);
 		importPrivateMsgs(f);
+		progressHolder.setProgress(77);
 		List<FileItem> files = importFiles(f);
+		progressHolder.setProgress(82);
 		importPolls(f);
+		progressHolder.setProgress(87);
 		importRoomFiles(f);
+		progressHolder.setProgress(92);
 
 		log.info("Room files import complete, starting copy of files and folders");
 		/*
 		 * ##################### Import real files and folders
 		 */
 		importFolders(f);
+		progressHolder.setProgress(97);
 
 		if (ver.compareTo(BackupVersion.get("4.0.0")) < 0) {
 			for (BaseFileItem bfi : files) {
@@ -434,6 +455,7 @@ public class BackupImport {
 		log.info("File explorer item import complete, clearing temp files");
 
 		FileUtils.deleteDirectory(f);
+		progressHolder.setProgress(100);
 	}
 
 	private static BackupVersion getVersion(Serializer ser, File f) {
diff --git a/openmeetings-install/src/main/java/org/apache/openmeetings/cli/Admin.java b/openmeetings-install/src/main/java/org/apache/openmeetings/cli/Admin.java
index 42f9f61..281a6ff 100644
--- a/openmeetings-install/src/main/java/org/apache/openmeetings/cli/Admin.java
+++ b/openmeetings-install/src/main/java/org/apache/openmeetings/cli/Admin.java
@@ -553,7 +553,7 @@ public class Admin {
 	private void processRestore(File backup) throws Exception {
 		try (InputStream is = new FileInputStream(backup)) {
 			BackupImport importCtrl = getApplicationContext().getBean(BackupImport.class);
-			importCtrl.performImport(is);
+			importCtrl.performImport(is, new ProgressHolder());
 		}
 	}
 
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/AdminActionsPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/AdminActionsPanel.java
index 5c36a26..3fc63da 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/AdminActionsPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/AdminActionsPanel.java
@@ -18,7 +18,7 @@
  */
 package org.apache.openmeetings.web.admin;
 
-import static org.apache.openmeetings.web.util.CallbackFunctionHelper.newOkCancelDangerConfirm;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelDangerConfirm;
 
 import org.apache.openmeetings.web.common.FormActionsPanel;
 import org.apache.wicket.ajax.AjaxRequestTarget;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/backup/BackupPanel.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/backup/BackupPanel.html
index d78d23c..f66e437 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/backup/BackupPanel.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/backup/BackupPanel.html
@@ -35,14 +35,13 @@
 				</div>
 			</div>
 			<div class="formelement">
-				<span wicket:id="progress"></span>
-				<div wicket:id="dprogress"></div>
+				<div class="m-3" wicket:id="progress"></div>
 			</div>
 			<div class="formelement">
 				<!-- Perform Download -->
 				<button wicket:id="download"></button>
 				<!-- Perform Upload -->
-				<button class="fileinput fileinput-new d-inline m-0 btn btn-file btn-outline-primary" data-provides="fileinput">
+				<button class="fileinput fileinput-new d-inline m-0 btn btn-file btn-outline-primary" data-provides="fileinput" wicket:id="upload">
 					<wicket:message key="1536"/>
 					<input class="uploadFileField" wicket:id="fileInput" type="file"/>
 				</button>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/backup/BackupPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/backup/BackupPanel.java
index 63de964..4dc1b3b 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/backup/BackupPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/backup/BackupPanel.java
@@ -25,7 +25,9 @@ import java.io.File;
 import java.nio.file.Path;
 import java.text.DecimalFormat;
 import java.time.Duration;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
 
 import org.apache.openmeetings.backup.BackupExport;
 import org.apache.openmeetings.backup.BackupImport;
@@ -33,17 +35,20 @@ import org.apache.openmeetings.backup.ProgressHolder;
 import org.apache.openmeetings.util.CalendarPatterns;
 import org.apache.openmeetings.util.OmFileHelper;
 import org.apache.openmeetings.web.admin.AdminBasePanel;
+import org.apache.openmeetings.web.util.ThreadHelper;
 import org.apache.openmeetings.web.util.upload.BootstrapFileUploadBehavior;
-import org.apache.wicket.ajax.AbstractAjaxTimerBehavior;
+import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
+import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
 import org.apache.wicket.extensions.ajax.AjaxDownloadBehavior;
-import org.apache.wicket.extensions.ajax.markup.html.form.upload.UploadProgressBar;
+import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.form.CheckBox;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.form.upload.FileUpload;
 import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.ResourceModel;
 import org.apache.wicket.request.resource.IResource;
@@ -53,11 +58,11 @@ import org.apache.wicket.util.lang.Bytes;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.googlecode.wicket.jquery.ui.widget.progressbar.ProgressBar;
-
 import de.agilecoders.wicket.core.markup.html.bootstrap.button.BootstrapAjaxButton;
 import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
 import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
+import de.agilecoders.wicket.core.markup.html.bootstrap.components.progress.UpdatableProgressBar;
+import de.agilecoders.wicket.core.markup.html.bootstrap.utilities.BackgroundColorBehavior;
 /**
  * Panel component to manage Backup Import/Export
  *
@@ -83,13 +88,33 @@ public class BackupPanel extends AdminBasePanel {
 	private class BackupForm extends Form<Void> {
 		private static final long serialVersionUID = 1L;
 		private final Model<Boolean> includeFilesInBackup = Model.of(true);
-		private FileUploadField fileUploadField;
-		private AbstractAjaxTimerBehavior timer;
-		private ProgressBar progressBar;
+		private final FileUploadField fileUploadField = new FileUploadField("fileInput", new IModel<List<FileUpload>>() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public void setObject(List<FileUpload> object) {
+				//no-op
+			}
+
+			@Override
+			public List<FileUpload> getObject() {
+				return new ArrayList<>();
+			}
+		}) {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			protected boolean forceCloseStreamsOnDetach() {
+				return false;
+			}
+		};
+		private UpdatableProgressBar progressBar;
 		private File backupFile;
 		private Throwable th = null;
-		private boolean started = false;
-		private ProgressHolder progressHolder;
+		private boolean modeDownload = false;
+		private final ProgressHolder progressHolder = new ProgressHolder();
+		private BootstrapAjaxButton download;
+		private final WebMarkupContainer upload = new WebMarkupContainer("upload");
 
 		public BackupForm(String id) {
 			super(id);
@@ -105,16 +130,13 @@ public class BackupPanel extends AdminBasePanel {
 			DecimalFormat formatter = new DecimalFormat("#,###.00");
 			add(new Label("MaxUploadSize", formatter.format(megaBytes)));
 
-			// Add one file input field
-			fileUploadField = new FileUploadField("fileInput");
-
 			add(new CheckBox("includeFilesInBackup", includeFilesInBackup));
 
 			// Set maximum size controlled by configuration
 			setMaxSize(Bytes.bytes(maxBytes));
 
 			// Add a component to download a file without page refresh
-			final AjaxDownloadBehavior download = new AjaxDownloadBehavior(new IResource() {
+			final AjaxDownloadBehavior downloader = new AjaxDownloadBehavior(new IResource() {
 				private static final long serialVersionUID = 1L;
 
 				@Override
@@ -131,25 +153,24 @@ public class BackupPanel extends AdminBasePanel {
 					}.respond(attributes);
 				}
 			});
-			add(download);
+			add(downloader);
 			// add an download button
-			add(new BootstrapAjaxButton("download", new ResourceModel("1066"), this, Buttons.Type.Outline_Primary) {
+			add(download = new BootstrapAjaxButton("download", new ResourceModel("1066"), this, Buttons.Type.Outline_Primary) {
 				private static final long serialVersionUID = 1L;
 
 				@Override
 				protected void onSubmit(AjaxRequestTarget target) {
+					modeDownload = true;
 					String dateString = "backup_" + CalendarPatterns.getTimeForStreamId(new Date());
 					backupFile = new File(OmFileHelper.getUploadBackupDir(), dateString + ".zip");
-					th = null;
-					started = true;
-					progressHolder = new ProgressHolder();
-
-					timer.restart(target);
-					new Thread(new BackupProcess(backupExport, includeFilesInBackup.getObject())
-						, "Openmeetings - " + dateString).start();
-
-					// repaint the feedback panel so that it is hidden
-					target.add(feedback, progressBar.setVisible(true));
+					startWithProgress(() -> {
+						try {
+							backupExport.performExport(backupFile, includeFilesInBackup.getObject(), progressHolder);
+						} catch (Exception e) {
+							log.error("Exception on panel backup download ", e);
+							th = e;
+						}
+					}, dateString, target);
 				}
 
 				@Override
@@ -158,51 +179,63 @@ public class BackupPanel extends AdminBasePanel {
 					target.add(feedback);
 				}
 			});
-			add(timer = new AbstractAjaxTimerBehavior(Duration.ofSeconds(1)) {
+			add(progressBar = new UpdatableProgressBar("progress", new Model<>(0), BackgroundColorBehavior.Color.Info, true) {
 				private static final long serialVersionUID = 1L;
 
 				@Override
-				protected void onTimer(AjaxRequestTarget target) {
-					if (!started) {
-						timer.stop(target);
-						return;
-					}
+				protected IModel<Integer> newValue() {
+					return Model.of(progressHolder.getProgress());
+				}
+
+				@Override
+				protected void onPostProcessTarget(IPartialPageRequestHandler target) {
 					if (th != null) {
-						timer.stop(target);
-						progressBar.setVisible(false);
+						stop(target);
 						feedback.error(th.getMessage());
-						target.add(feedback);
-					} else {
-						progressBar.setModelObject(progressHolder.getProgress());
-						progressBar.refresh(target);
+						onComplete(target);
 					}
+					super.onPostProcessTarget(target);
 				}
-			});
-			add((progressBar = new ProgressBar("dprogress", new Model<>(0)) {
-				private static final long serialVersionUID = 1L;
 
 				@Override
-				protected void onComplete(AjaxRequestTarget target) {
-					timer.stop(target);
-					target.add(progressBar.setVisible(false));
-
-					download.initiate(target);
+				protected void onComplete(IPartialPageRequestHandler target) {
+					progressBar.setVisible(false);
+					target.add(feedback);
+					updateButtons(target, true);
+					if (modeDownload) {
+						downloader.initiate(target);
+					}
+					super.onComplete(target);
 				}
-			}).setVisible(false).setOutputMarkupPlaceholderTag(true));
-			add(fileUploadField.add(new AjaxFormSubmitBehavior(this, "change") {
+			});
+			progressBar.updateInterval(Duration.ofSeconds(1)).stop(null).striped(false).setVisible(false).setOutputMarkupPlaceholderTag(true);
+			upload.add(fileUploadField.add(new AjaxFormSubmitBehavior(this, "change") {
 				private static final long serialVersionUID = 1L;
 
 				@Override
 				protected void onSubmit(AjaxRequestTarget target) {
 					FileUpload upload = fileUploadField.getFileUpload();
+					modeDownload = false;
 					try {
-						if (upload == null || upload.getInputStream() == null) {
-							feedback.error("File is empty");
-							target.add(feedback);
-							return;
-						}
-						backupImport.performImport(upload.getInputStream());
-						feedback.success(getString("387") + " - " + getString("54"));
+						startWithProgress(() -> {
+							try {
+								if (upload == null || upload.getInputStream() == null) {
+									feedback.error("File is empty");
+									target.add(feedback);
+									return;
+								}
+								backupImport.performImport(upload.getInputStream(), progressHolder);
+								feedback.success(getString("387") + " - " + getString("54"));
+							} catch (Exception e) {
+								log.error("Exception on panel backup download ", e);
+								th = e;
+							} finally {
+								if (upload != null) {
+									upload.closeStreams();
+									upload.delete();
+								}
+							}
+						}, "Restore", target);
 					} catch (Exception e) {
 						log.error("Exception on panel backup upload ", e);
 						feedback.error(e);
@@ -217,48 +250,45 @@ public class BackupPanel extends AdminBasePanel {
 					target.add(feedback);
 				}
 			}));
-			add(new Label("cmdLineDesc", getString("1505")).setEscapeModelStrings(false).setRenderBodyOnly(true));
+			add(upload.setOutputMarkupId(true), new Label("cmdLineDesc", getString("1505")).setEscapeModelStrings(false).setRenderBodyOnly(true));
 			super.onInitialize();
 		}
 
-		@Override
-		protected void onDetach() {
-			includeFilesInBackup.detach();
-			super.onDetach();
+		private void updateButtons(IPartialPageRequestHandler target, boolean enabled) {
+			download.setEnabled(enabled);
+			upload.add(enabled ? AttributeModifier.remove("disabled") : AttributeModifier.append("disabled", "disabled"));
+			fileUploadField.setEnabled(enabled);
+			target.add(download, upload);
 		}
 
-		private class BackupProcess implements Runnable {
-			private BackupExport backup;
-			private boolean includeFiles;
+		private void startWithProgress(Runnable r, String label, AjaxRequestTarget target) {
+			th = null;
+			progressHolder.setProgress(0);
 
-			public BackupProcess(BackupExport backup, boolean includeFiles) {
-				this.backup = backup;
-				this.includeFiles = includeFiles;
-				th = null;
-			}
+			ThreadHelper.startRunnable(r, "Openmeetings - " + label);
 
-			@Override
-			public void run() {
-				try {
-					backup.performExport(backupFile, includeFiles, progressHolder);
-				} catch (Exception e) {
-					log.error("Exception on panel backup download ", e);
-					th = e;
-				}
-			}
+			// repaint the feedback panel so that it is hidden
+			updateButtons(target, false);
+			target.add(feedback, progressBar.restart(target).setModelObject(0).setVisible(true));
+		}
+
+		@Override
+		protected void onDetach() {
+			includeFilesInBackup.detach();
+			super.onDetach();
 		}
 	}
 
 	public BackupPanel(String id) {
 		super(id);
+	}
 
+	@Override
+	protected void onInitialize() {
+		super.onInitialize();
 		add(feedback.setOutputMarkupId(true));
 
-		BackupForm backupForm = new BackupForm("backupUpload");
-
-		backupForm.add(new UploadProgressBar("progress", backupForm, backupForm.fileUploadField));
-
-		add(backupForm);
+		add(new BackupForm("backupUpload"));
 		add(BootstrapFileUploadBehavior.INSTANCE);
 	}
 }
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java
index b3576ea..3650099 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java
@@ -19,7 +19,7 @@
 package org.apache.openmeetings.web.admin.connection;
 
 import static org.apache.openmeetings.util.OpenmeetingsVariables.ATTR_CLASS;
-import static org.apache.openmeetings.web.util.CallbackFunctionHelper.newOkCancelConfirmCfg;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelConfirm;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -34,7 +34,6 @@ import org.apache.openmeetings.web.admin.AdminBasePanel;
 import org.apache.openmeetings.web.admin.SearchableDataView;
 import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.common.PagedEntityListPanel;
-import org.apache.openmeetings.web.common.confirmation.ConfirmationBehavior;
 import org.apache.openmeetings.web.data.SearchableDataProvider;
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.ajax.AjaxEventBehavior;
@@ -98,9 +97,6 @@ public class ConnectionsPanel extends AdminBasePanel {
 					private static final long serialVersionUID = 1L;
 					{
 						setSize(Buttons.Size.Small);
-						add(new ConfirmationBehavior(/*TODO https://github.com/l0rdn1kk0n/wicket-bootstrap/issues/845 ".om-kick-btn", */newOkCancelConfirmCfg(container, getString("605"))
-								.withSingleton(true)
-								));
 					}
 
 					@Override
@@ -108,7 +104,7 @@ public class ConnectionsPanel extends AdminBasePanel {
 						cm.invalidate(c.getUserId(), c.getSessionId());
 						target.add(container, details.setVisible(false));
 					}
-				});
+				}.add(newOkCancelConfirm(this, getString("605"))));
 				item.add(new AjaxEventBehavior(EVT_CLICK) {
 					private static final long serialVersionUID = 1L;
 
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/email/EmailForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/email/EmailForm.java
index 48c6509..cfbdb03 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/email/EmailForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/email/EmailForm.java
@@ -18,7 +18,7 @@
  */
 package org.apache.openmeetings.web.admin.email;
 
-import static org.apache.openmeetings.web.util.CallbackFunctionHelper.newOkCancelDangerConfirm;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelDangerConfirm;
 
 import org.apache.openmeetings.db.dao.basic.MailMessageDao;
 import org.apache.openmeetings.db.entity.basic.MailMessage;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/groups/GroupUsersPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/groups/GroupUsersPanel.java
index 8f5c39c..fe55167 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/groups/GroupUsersPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/groups/GroupUsersPanel.java
@@ -18,6 +18,8 @@
  */
 package org.apache.openmeetings.web.admin.groups;
 
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelDangerConfirm;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -29,7 +31,6 @@ import org.apache.openmeetings.web.admin.SearchableDataView;
 import org.apache.openmeetings.web.app.WebSession;
 import org.apache.openmeetings.web.common.PagedEntityListPanel;
 import org.apache.openmeetings.web.data.SearchableDataProvider;
-import org.apache.openmeetings.web.util.CallbackFunctionHelper;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
 import org.apache.wicket.markup.html.basic.Label;
@@ -92,7 +93,7 @@ public class GroupUsersPanel extends Panel {
 					}
 				};
 				del.setIconType(FontAwesome5IconType.times_s)
-						.add(CallbackFunctionHelper.newOkCancelDangerConfirm(this, getString("833")));
+						.add(newOkCancelDangerConfirm(this, getString("833")));
 				item.add(del);
 				item.add(new BootstrapBadge("new", new ResourceModel("lbl.new"), BadgeBehavior.Type.Warning).setVisible((grpUser.getId() == null)));
 			}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/labels/LangPanel.html b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/labels/LangPanel.html
index 7197bfd..3096f3a 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/labels/LangPanel.html
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/labels/LangPanel.html
@@ -38,7 +38,6 @@
 						<wicket:message key="387"/>
 						<input type="file" accept="text/xml" wicket:id="fileInput"/>
 					</button>
-					<span wicket:id="progress">[progressbar]</span>
 				</div>
 			</div>
 		</form>
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/labels/LangPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/labels/LangPanel.java
index cdb6f5e..8c4791d 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/labels/LangPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/labels/LangPanel.java
@@ -20,6 +20,7 @@ package org.apache.openmeetings.web.admin.labels;
 
 import static java.time.Duration.ZERO;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.ATTR_CLASS;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelDangerConfirm;
 import static org.apache.wicket.request.resource.ContentDisposition.ATTACHMENT;
 
 import java.io.IOException;
@@ -39,7 +40,6 @@ import org.apache.openmeetings.web.common.PagedEntityListPanel;
 import org.apache.openmeetings.web.data.DataViewContainer;
 import org.apache.openmeetings.web.data.OmOrderByBorder;
 import org.apache.openmeetings.web.data.SearchableDataProvider;
-import org.apache.openmeetings.web.util.CallbackFunctionHelper;
 import org.apache.openmeetings.web.util.upload.BootstrapFileUploadBehavior;
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.ajax.AjaxEventBehavior;
@@ -47,7 +47,6 @@ import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
 import org.apache.wicket.ajax.markup.html.AjaxLink;
 import org.apache.wicket.extensions.ajax.AjaxDownloadBehavior;
-import org.apache.wicket.extensions.ajax.markup.html.form.upload.UploadProgressBar;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.form.upload.FileUpload;
@@ -151,7 +150,6 @@ public class LangPanel extends AdminBasePanel {
 		add(navigator);
 		langForm = new LangForm("langForm", listContainer, this);
 		langForm.add(fileUploadField);
-		langForm.add(new UploadProgressBar("progress", langForm, fileUploadField));
 		fileUploadField.add(new AjaxFormSubmitBehavior(langForm, "change") {
 			private static final long serialVersionUID = 1L;
 
@@ -249,7 +247,7 @@ public class LangPanel extends AdminBasePanel {
 			}
 		};
 		langForm.add(delLngBtn.setIconType(FontAwesome5IconType.times_s)
-				.add(CallbackFunctionHelper.newOkCancelDangerConfirm(this, getString("833"))));
+				.add(newOkCancelDangerConfirm(this, getString("833"))));
 		super.onInitialize();
 	}
 
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/oauth/OAuthForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/oauth/OAuthForm.java
index 844d9e5..d1e3728 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/oauth/OAuthForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/oauth/OAuthForm.java
@@ -19,6 +19,7 @@
 package org.apache.openmeetings.web.admin.oauth;
 
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelDangerConfirm;
 import static org.apache.openmeetings.web.pages.auth.SignInPage.getRedirectUri;
 
 import java.util.AbstractMap.SimpleEntry;
@@ -32,7 +33,6 @@ import org.apache.openmeetings.db.entity.server.OAuthServer;
 import org.apache.openmeetings.db.entity.server.OAuthServer.RequestInfoMethod;
 import org.apache.openmeetings.db.entity.server.OAuthServer.RequestTokenMethod;
 import org.apache.openmeetings.web.admin.AdminBaseForm;
-import org.apache.openmeetings.web.util.CallbackFunctionHelper;
 import org.apache.wicket.Component;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.html.WebMarkupContainer;
@@ -80,7 +80,7 @@ public class OAuthForm extends AdminBaseForm<OAuthServer> {
 				}
 			};
 			del.setIconType(FontAwesome5IconType.times_s)
-					.add(CallbackFunctionHelper.newOkCancelDangerConfirm(this, getString("833")));
+					.add(newOkCancelDangerConfirm(this, getString("833")));
 			item.add(new Label("key", Model.of(entry.getKey())))
 				.add(new Label("value", Model.of(entry.getValue())))
 				.add(del);
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
index 5e1a0eb..db27b57 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
@@ -23,6 +23,7 @@ import static org.apache.openmeetings.web.admin.AdminUserChoiceProvider.PAGE_SIZ
 import static org.apache.openmeetings.web.app.Application.kickUser;
 import static org.apache.openmeetings.web.app.WebSession.getRights;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelDangerConfirm;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -48,7 +49,6 @@ import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.web.admin.AdminBaseForm;
 import org.apache.openmeetings.web.admin.AdminUserChoiceProvider;
 import org.apache.openmeetings.web.app.ClientManager;
-import org.apache.openmeetings.web.util.CallbackFunctionHelper;
 import org.apache.openmeetings.web.util.RestrictiveChoiceProvider;
 import org.apache.openmeetings.web.util.RoomTypeDropDown;
 import org.apache.wicket.ajax.AjaxRequestTarget;
@@ -110,7 +110,7 @@ public class RoomForm extends AdminBaseForm<Room> {
 				}
 			};
 			del.setIconType(FontAwesome5IconType.times_s)
-					.add(CallbackFunctionHelper.newOkCancelDangerConfirm(this, getString("833")));
+					.add(newOkCancelDangerConfirm(this, getString("833")));
 			item.add(new Label("clientId", "" + c.getUserId()))
 				.add(new Label("clientLogin", "" + c.getUser().getLogin()))
 				.add(del);
@@ -318,7 +318,7 @@ public class RoomForm extends AdminBaseForm<Room> {
 					}
 				};
 				del.setIconType(FontAwesome5IconType.times_s)
-						.add(CallbackFunctionHelper.newOkCancelDangerConfirm(this, getString("833")));
+						.add(newOkCancelDangerConfirm(this, getString("833")));
 				item.add(new CheckBox("superModerator", new PropertyModel<Boolean>(moderator, "superModerator")))
 					.add(new Label("userId", String.valueOf(moderator.getUser().getId())))
 					.add(name)
@@ -404,7 +404,7 @@ public class RoomForm extends AdminBaseForm<Room> {
 					}
 				};
 				del.setIconType(FontAwesome5IconType.times_s)
-						.add(CallbackFunctionHelper.newOkCancelDangerConfirm(this, getString("833")));
+						.add(newOkCancelDangerConfirm(this, getString("833")));
 				item.add(new Label("name", new PropertyModel<>(rf.getFile(), "name")))
 					.add(new Label("wbIdx", new PropertyModel<>(rf, "wbIdx")))
 					.add(del);
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/FormActionsPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/FormActionsPanel.java
index 56a7783..48e268d 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/FormActionsPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/FormActionsPanel.java
@@ -18,7 +18,7 @@
  */
 package org.apache.openmeetings.web.common;
 
-import static org.apache.openmeetings.web.util.CallbackFunctionHelper.newOkCancelDangerConfirm;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelDangerConfirm;
 
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.markup.html.AjaxLink;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java
index 427114a..6a0d079 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java
@@ -24,9 +24,9 @@ import static org.apache.openmeetings.util.OpenmeetingsVariables.ATTR_CLASS;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_MYROOMS_ENABLED;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_USER_ID;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelConfirm;
 import static org.apache.openmeetings.web.util.CallbackFunctionHelper.getNamedFunction;
 import static org.apache.openmeetings.web.util.CallbackFunctionHelper.getParam;
-import static org.apache.openmeetings.web.util.CallbackFunctionHelper.newOkCancelConfirm;
 import static org.apache.openmeetings.web.util.OmUrlFragment.CHILD_ID;
 import static org.apache.openmeetings.web.util.OmUrlFragment.PROFILE_EDIT;
 import static org.apache.openmeetings.web.util.OmUrlFragment.PROFILE_MESSAGES;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/UploadableImagePanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/UploadableImagePanel.java
index 7fdd0de..36bb7e8 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/UploadableImagePanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/UploadableImagePanel.java
@@ -19,7 +19,7 @@
 package org.apache.openmeetings.web.common;
 
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getMaxUploadSize;
-import static org.apache.openmeetings.web.util.CallbackFunctionHelper.newOkCancelConfirm;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelConfirm;
 
 import java.io.File;
 import java.util.Optional;
@@ -35,7 +35,6 @@ import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.form.upload.FileUpload;
 import org.apache.wicket.markup.html.form.upload.FileUploadField;
-import org.apache.wicket.markup.html.panel.EmptyPanel;
 import org.apache.wicket.model.util.ListModel;
 import org.apache.wicket.util.lang.Bytes;
 import org.slf4j.Logger;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/confirmation/ConfirmableAjaxBorder.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/confirmation/ConfirmableAjaxBorder.java
index dab2c01..a5039c3 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/confirmation/ConfirmableAjaxBorder.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/confirmation/ConfirmableAjaxBorder.java
@@ -20,6 +20,7 @@ package org.apache.openmeetings.web.common.confirmation;
 
 import static org.apache.openmeetings.web.common.BasePanel.EVT_CLICK;
 
+import org.apache.wicket.Component;
 import org.apache.wicket.ajax.AjaxEventBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
@@ -114,4 +115,22 @@ public abstract class ConfirmableAjaxBorder extends Border {
 		title.detach();
 		message.detach();
 	}
+
+	public static ConfirmationBehavior newOkCancelDangerConfirm(Component c, String title) {
+		return new ConfirmationBehavior(newOkCancelConfirmCfg(c, title)
+				.withBtnOkClass("btn btn-sm btn-danger")
+				.withBtnOkIconClass("fas fa-exclamation-triangle")
+				);
+	}
+
+	public static ConfirmationBehavior newOkCancelConfirm(Component c, String title) {
+		return new ConfirmationBehavior(newOkCancelConfirmCfg(c, title));
+	}
+
+	public static ConfirmationConfig newOkCancelConfirmCfg(Component c, String title) {
+		return new ConfirmationConfig()
+				.withBtnCancelLabel(c.getString("lbl.cancel"))
+				.withBtnOkLabel(c.getString("54"))
+				.withTitle(title);
+	}
 }
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 34e4321..b2d9b56 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
@@ -221,7 +221,7 @@ public class RoomSidebar extends Panel {
 	}
 
 	public void showUpload(IPartialPageRequestHandler handler) {
-		upload.open(handler);
+		upload.show(handler);
 	}
 
 	public void setFilesActive(IPartialPageRequestHandler handler) {
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 aeeea96..181cf1d 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
@@ -20,18 +20,17 @@
 -->
 <!DOCTYPE html>
 <html xmlns:wicket="http://wicket.apache.org">
-<wicket:panel>
+<wicket:extend>
 	<form wicket:id="form">
 		<div class="mb-4"><wicket:message key="594"/></div>
-		<div class="fileinput fileinput-new m-0" data-provides="fileinput">
+		<div class="fileinput fileinput-new d-inline m-0" data-provides="fileinput">
 			<div class="fileinput-filename d-block"></div>
-			<span class="btn btn-file">
+			<span class="btn btn-file btn-outline-primary">
 				<span><wicket:message key="596"/></span>
 				<input type="file" multiple="multiple" wicket:id="file"/>
 			</span>
 		</div>
 		<div wicket:id="feedback" class="error"></div>
-		<span wicket:id="progress">[progressbar]</span>
 		<div class="mt-3">
 			<input wicket:id="to-wb" type="checkbox"/><label wicket:for="to-wb"><wicket:message key="1312"/></label>
 			<div class="ml-3" wicket:id="clean-block">
@@ -39,9 +38,9 @@
 			</div>
 		</div>
 	</form>
-	<div wicket:id="convProgress"></div>
+	<div wicket:id="progress"></div>
 	<form wicket:id="name-form">
 		<input type="hidden" wicket:id="name"/>
 	</form>
-</wicket:panel>
+</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 eecc5b4..8ca54c4 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
@@ -23,7 +23,6 @@ import static org.apache.openmeetings.web.app.WebSession.getUserId;
 
 import java.time.Duration;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Optional;
 import java.util.function.DoubleConsumer;
@@ -34,17 +33,13 @@ import org.apache.openmeetings.db.entity.file.BaseFileItem;
 import org.apache.openmeetings.db.entity.file.FileItem;
 import org.apache.openmeetings.util.process.ProcessResult;
 import org.apache.openmeetings.util.process.ProcessResultList;
-import org.apache.openmeetings.web.app.Application;
-import org.apache.openmeetings.web.app.WebSession;
 import org.apache.openmeetings.web.room.RoomPanel;
+import org.apache.openmeetings.web.util.ThreadHelper;
 import org.apache.openmeetings.web.util.upload.BootstrapFileUploadBehavior;
-import org.apache.wicket.ThreadContext;
-import org.apache.wicket.ajax.AbstractAjaxTimerBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
 import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
 import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
-import org.apache.wicket.extensions.ajax.markup.html.form.upload.UploadProgressBar;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.JavaScriptHeaderItem;
 import org.apache.wicket.markup.head.PriorityHeaderItem;
@@ -56,7 +51,7 @@ import org.apache.wicket.markup.html.form.upload.FileUpload;
 import org.apache.wicket.markup.html.form.upload.FileUploadField;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
-import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.model.ResourceModel;
 import org.apache.wicket.request.resource.JavaScriptResourceReference;
 import org.apache.wicket.spring.injection.annot.SpringBean;
 import org.apache.wicket.util.lang.Bytes;
@@ -64,13 +59,15 @@ import org.apache.wicket.util.string.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.googlecode.wicket.jquery.ui.widget.dialog.AbstractFormDialog;
-import com.googlecode.wicket.jquery.ui.widget.dialog.DialogButton;
-import com.googlecode.wicket.jquery.ui.widget.progressbar.ProgressBar;
-
+import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
 import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;
+import de.agilecoders.wicket.core.markup.html.bootstrap.components.progress.UpdatableProgressBar;
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.ModalCloseButton;
+import de.agilecoders.wicket.core.markup.html.bootstrap.utilities.BackgroundColorBehavior;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.spinner.SpinnerAjaxButton;
 
-public class UploadDialog extends AbstractFormDialog<String> {
+public class UploadDialog extends Modal<String> {
 	private static final long serialVersionUID = 1L;
 	private static final Logger log = LoggerFactory.getLogger(UploadDialog.class);
 	private final NotificationPanel feedback = new NotificationPanel("feedback");
@@ -87,8 +84,7 @@ public class UploadDialog extends AbstractFormDialog<String> {
 			return true;
 		}
 	};
-	private DialogButton upload;
-	private DialogButton cancel;
+	private SpinnerAjaxButton upload;
 	private final FileUploadField uploadField = new FileUploadField("file", new IModel<List<FileUpload>>() {
 		private static final long serialVersionUID = 1L;
 
@@ -121,52 +117,40 @@ public class UploadDialog extends AbstractFormDialog<String> {
 	@SpringBean
 	private FileItemLogDao fileLogDao;
 
-	private final AbstractAjaxTimerBehavior timer = new AbstractAjaxTimerBehavior(Duration.ofSeconds(1)) {
+	private final UpdatableProgressBar progressBar = new UpdatableProgressBar("progress", new Model<>(0), BackgroundColorBehavior.Color.Info, true) {
 		private static final long serialVersionUID = 1L;
 
 		@Override
-		protected void onTimer(AjaxRequestTarget target) {
-			if (progress == null) {
-				timer.stop(target);
-				return;
-			}
-			if (progress.intValue() == 100) {
-				timer.stop(target);
-				target.add(progressBar.setVisible(false));
-				room.getSidebar().updateFiles(target);
-				if (form.hasError()) {
-					setTitle(target, getString("upload.dlg.choose.title"));
-					target.add(form.setVisible(true));
-					onError(target, null);
-				} else {
-					close(target, null);
-				}
-			} else {
-				progressBar.setModelObject(progress);
-				progressBar.refresh(target);
-			}
+		protected IModel<Integer> newValue() {
+			return Model.of(progress);
 		}
-	};
-	private final ProgressBar progressBar = new ProgressBar("convProgress", new Model<>(0)) {
-		private static final long serialVersionUID = 1L;
 
 		@Override
-		protected void onComplete(AjaxRequestTarget target) {
-			timer.stop(target);
+		protected void onComplete(IPartialPageRequestHandler target) {
 			progressBar.setVisible(false);
+			room.getSidebar().updateFiles(target);
+			if (form.hasError()) {
+				target.add(form.setVisible(true));
+				target.add(feedback);
+			} else {
+				close(target);
+			}
 			target.add(progressBar);
 		}
 	};
-	private Integer progress;
+	private int progress = 0;
 
 	public UploadDialog(String id, RoomPanel room, RoomFilePanel roomFiles) {
-		super(id, "");
+		super(id);
 		this.roomFiles = roomFiles;
 		this.room = room;
 	}
 
 	@Override
 	protected void onInitialize() {
+		header(new ResourceModel("upload.dlg.choose.title"));
+		setBackdrop(Backdrop.STATIC);
+
 		add(form.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true));
 		toWb.add(new OnChangeAjaxBehavior() {
 			private static final long serialVersionUID = 1L;
@@ -190,88 +174,52 @@ public class UploadDialog extends AbstractFormDialog<String> {
 			@Override
 			protected void onSubmit(AjaxRequestTarget target) {
 				if (!Strings.isEmpty(getComponent().getDefaultModelObjectAsString())) {
-					upload.setEnabled(true, target);
+					target.add(upload.setEnabled(true));
 				}
 			}
 		}).setOutputMarkupId(true);
-		form.add(new UploadProgressBar("progress", form, uploadField));
 
 		add(nameForm.add(fileName.setOutputMarkupId(true)));
 		add(BootstrapFileUploadBehavior.INSTANCE);
-		getTitle().setObject(getString("upload.dlg.choose.title"));
-		upload = new DialogButton("upload", getString("593"), false) {
+		addButton(upload = new SpinnerAjaxButton("button", new ResourceModel("593"), form, Buttons.Type.Outline_Primary) {
 			private static final long serialVersionUID = 1L;
 
 			@Override
-			public boolean isIndicating() {
-				return true;
+			protected void onError(AjaxRequestTarget target) {
+				target.add(feedback);
 			}
-		};
-		cancel = new DialogButton("close", getString("85"));
-
-		add(progressBar.setOutputMarkupPlaceholderTag(true).setVisible(false));
-		add(timer);
-		super.onInitialize();
-	}
 
-	@Override
-	public void onClick(AjaxRequestTarget target, DialogButton button) {
-		if (button == null || button.match("close")) {
-			super.onClick(target, button);
-		}
-	}
+			@Override
+			protected void onSubmit(AjaxRequestTarget target) {
+				List<FileUpload> ful = uploadField.getFileUploads();
+				if (ful != null) {
 
-	@Override
-	protected List<DialogButton> getButtons() {
-		return Arrays.asList(upload, cancel);
-	}
+					progress = 0;
+					progressBar.restart(target);
+					target.add(
+							progressBar.setModelObject(progress).setVisible(true)
+							, form.setVisible(false)
+							, upload.setEnabled(false));
 
-	@Override
-	public DialogButton getSubmitButton() {
-		return upload;
-	}
+					ThreadHelper.startRunnable(UploadDialog.this::convertAll);
+				}
+			}
+		});
+		upload.setEnabled(false);
+		addButton(new ModalCloseButton(new ResourceModel("85")).type(Buttons.Type.Outline_Secondary));
 
-	@Override
-	public Form<?> getForm() {
-		return form;
+		progressBar.updateInterval(Duration.ofSeconds(1)).stop(null).striped(false);
+		add(progressBar.setOutputMarkupPlaceholderTag(true).setVisible(false));
+		super.onInitialize();
 	}
 
 	@Override
-	protected void onOpen(IPartialPageRequestHandler handler) {
-		setTitle(handler, getString("upload.dlg.choose.title"));
-		super.onOpen(handler);
-		upload.setEnabled(true, handler);
+	public Modal<String> show(IPartialPageRequestHandler handler) {
+		handler.add(upload.setEnabled(true));
 		uploadField.setModelObject(new ArrayList<>());
 		handler.add(form.setVisible(true), fileName);
 		handler.appendJavaScript(String.format("bindUpload('%s', '%s');", form.getMarkupId(), fileName.getMarkupId()));
-	}
-
-	@Override
-	protected void onError(AjaxRequestTarget target, DialogButton btn) {
-		target.add(feedback);
-	}
-
-	@Override
-	protected void onSubmit(AjaxRequestTarget target, DialogButton btn) {
-		List<FileUpload> ful = uploadField.getFileUploads();
-		if (ful != null) {
-
-			progress = 0;
-			timer.restart(target);
-			setTitle(target, getString("upload.dlg.convert.title"));
-			target.add(progressBar.setModelObject(progress).setVisible(true), form.setVisible(false));
-
-			final Application app = Application.get();
-			final WebSession session = WebSession.get();
-			final RequestCycle rc = RequestCycle.get();
-			new Thread(() -> {
-				ThreadContext.setApplication(app);
-				ThreadContext.setSession(session);
-				ThreadContext.setRequestCycle(rc);
-				convertAll();
-				ThreadContext.detach();
-			}).start();
-		}
+		return super.show(handler);
 	}
 
 	@Override
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/calendar/CalendarDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/calendar/CalendarDialog.java
index 78350d7..7e60bcc 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/calendar/CalendarDialog.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/calendar/CalendarDialog.java
@@ -19,7 +19,7 @@
 package org.apache.openmeetings.web.user.calendar;
 
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
-import static org.apache.openmeetings.web.util.CallbackFunctionHelper.newOkCancelDangerConfirm;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelDangerConfirm;
 
 import java.util.List;
 
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatToolbar.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatToolbar.java
index 7b8e6a2..7858308 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatToolbar.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatToolbar.java
@@ -26,8 +26,8 @@ import static org.apache.openmeetings.web.app.WebSession.getDateFormat;
 import static org.apache.openmeetings.web.app.WebSession.getRights;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 import static org.apache.openmeetings.web.common.BasePanel.EVT_CLICK;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelDangerConfirm;
 import static org.apache.openmeetings.web.room.RoomPanel.isModerator;
-import static org.apache.openmeetings.web.util.CallbackFunctionHelper.newOkCancelDangerConfirm;
 
 import java.util.List;
 
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/dashboard/admin/AdminCleanupInfoDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/dashboard/admin/AdminCleanupInfoDialog.java
index 86824a3..c305a4d 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/dashboard/admin/AdminCleanupInfoDialog.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/dashboard/admin/AdminCleanupInfoDialog.java
@@ -26,7 +26,7 @@ import static org.apache.openmeetings.cli.CleanupHelper.getRecUnit;
 import static org.apache.openmeetings.util.OmFileHelper.getHumanSize;
 import static org.apache.openmeetings.util.OmFileHelper.getStreamsDir;
 import static org.apache.openmeetings.util.OmFileHelper.getUploadDir;
-import static org.apache.openmeetings.web.util.CallbackFunctionHelper.newOkCancelDangerConfirm;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelDangerConfirm;
 
 import org.apache.openmeetings.cli.CleanupEntityUnit;
 import org.apache.openmeetings.cli.CleanupUnit;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/InvitationDetails.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/InvitationDetails.java
index f71a50c..6d31180 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/InvitationDetails.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/InvitationDetails.java
@@ -20,7 +20,7 @@ package org.apache.openmeetings.web.user.profile;
 
 import static org.apache.openmeetings.db.util.FormatHelper.formatUser;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
-import static org.apache.openmeetings.web.util.CallbackFunctionHelper.newOkCancelDangerConfirm;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelDangerConfirm;
 
 import java.util.Date;
 
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/MessagesContactsPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/MessagesContactsPanel.java
index 5bf1975..e5c7e0f 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/MessagesContactsPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/MessagesContactsPanel.java
@@ -24,6 +24,7 @@ import static org.apache.openmeetings.db.entity.user.PrivateMessage.TRASH_FOLDER
 import static org.apache.openmeetings.util.OpenmeetingsVariables.ATTR_CLASS;
 import static org.apache.openmeetings.web.app.WebSession.getDateFormat;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
+import static org.apache.openmeetings.web.common.confirmation.ConfirmableAjaxBorder.newOkCancelDangerConfirm;
 import static org.apache.openmeetings.web.util.CallbackFunctionHelper.addOnClick;
 
 import java.util.ArrayList;
@@ -53,7 +54,6 @@ import org.apache.openmeetings.web.data.OmOrderByBorder;
 import org.apache.openmeetings.web.data.SearchableDataProvider;
 import org.apache.openmeetings.web.user.MessageDialog;
 import org.apache.openmeetings.web.user.rooms.RoomEnterBehavior;
-import org.apache.openmeetings.web.util.CallbackFunctionHelper;
 import org.apache.openmeetings.web.util.ContactsHelper;
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.Component;
@@ -232,7 +232,7 @@ public class MessagesContactsPanel extends UserBasePanel {
 					}
 				};
 				del.setIconType(FontAwesome5IconType.times_s)
-						.add(CallbackFunctionHelper.newOkCancelDangerConfirm(this, getString("833")));
+						.add(newOkCancelDangerConfirm(this, getString("833")));
 				item.add(del);
 				item.add(new AjaxEventBehavior(EVT_CLICK) {
 					private static final long serialVersionUID = 1L;
@@ -499,7 +499,7 @@ public class MessagesContactsPanel extends UserBasePanel {
 					}
 				};
 				del.setIconType(FontAwesome5IconType.times_s)
-						.add(CallbackFunctionHelper.newOkCancelDangerConfirm(this, getString("833")));
+						.add(newOkCancelDangerConfirm(this, getString("833")));
 				item.add(del.setVisible(!uc.isPending()));
 			}
 		};
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/CallbackFunctionHelper.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/CallbackFunctionHelper.java
index cca93f8..c9d1808 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/CallbackFunctionHelper.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/CallbackFunctionHelper.java
@@ -20,8 +20,6 @@ import static java.util.UUID.randomUUID;
 
 import java.io.Serializable;
 
-import org.apache.openmeetings.web.common.confirmation.ConfirmationBehavior;
-import org.apache.openmeetings.web.common.confirmation.ConfirmationConfig;
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.Component;
 import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
@@ -64,22 +62,4 @@ public class CallbackFunctionHelper {
 	public static AttributeModifier addOnClick(Serializable handler) {
 		return AttributeModifier.replace("onclick", handler);
 	}
-
-	public static ConfirmationConfig newOkCancelConfirmCfg(Component c, String title) {
-		return new ConfirmationConfig()
-				.withBtnCancelLabel(c.getString("lbl.cancel"))
-				.withBtnOkLabel(c.getString("54"))
-				.withTitle(title);
-	}
-
-	public static ConfirmationBehavior newOkCancelConfirm(Component c, String title) {
-		return new ConfirmationBehavior(newOkCancelConfirmCfg(c, title));
-	}
-
-	public static ConfirmationBehavior newOkCancelDangerConfirm(Component c, String title) {
-		return new ConfirmationBehavior(newOkCancelConfirmCfg(c, title)
-				.withBtnOkClass("btn btn-sm btn-danger")
-				.withBtnOkIconClass("fas fa-exclamation-triangle")
-				);
-	}
 }
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/ThreadHelper.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/ThreadHelper.java
new file mode 100644
index 0000000..6415838
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/util/ThreadHelper.java
@@ -0,0 +1,48 @@
+/*
+ * 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.util;
+
+import org.apache.openmeetings.web.app.Application;
+import org.apache.openmeetings.web.app.WebSession;
+import org.apache.wicket.ThreadContext;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.util.string.Strings;
+
+public class ThreadHelper {
+	private ThreadHelper() {}
+
+	public static void startRunnable(Runnable r) {
+		startRunnable(r, null);
+	}
+
+	public static void startRunnable(Runnable r, String name) {
+		final Application app = Application.get();
+		final WebSession session = WebSession.get();
+		final RequestCycle rc = RequestCycle.get();
+		Thread t = new Thread(() -> {
+			ThreadContext.setApplication(app);
+			ThreadContext.setSession(session);
+			ThreadContext.setRequestCycle(rc);
+			r.run();
+			ThreadContext.detach();
+		});
+		if (!Strings.isEmpty(name)) {
+			t.setName(name);
+		}
+		t.start();
+	}
+}
diff --git a/openmeetings-web/src/main/webapp/css/raw-general.css b/openmeetings-web/src/main/webapp/css/raw-general.css
index df4f6ad..dc0d250 100644
--- a/openmeetings-web/src/main/webapp/css/raw-general.css
+++ b/openmeetings-web/src/main/webapp/css/raw-general.css
@@ -286,11 +286,6 @@ html, body {
 	margin-left: 10px;
 	padding-left: 10px;
 }
-.ui-progressbar div {
-	text-align: center;
-	padding-top: 7px;
-	font-weight: bold;
-}
 table.messages {
 	width: 100%;
 	min-height: 500px;
diff --git a/openmeetings-web/src/test/java/org/apache/openmeetings/backup/TestBackup.java b/openmeetings-web/src/test/java/org/apache/openmeetings/backup/TestBackup.java
index a9c6ba6..80057b1 100644
--- a/openmeetings-web/src/test/java/org/apache/openmeetings/backup/TestBackup.java
+++ b/openmeetings-web/src/test/java/org/apache/openmeetings/backup/TestBackup.java
@@ -94,7 +94,7 @@ public class TestBackup extends AbstractJUnitDefaults {
 			String name = backup.getName();
 			log.debug("Import of backup file : '" + name + "' is started ...");
 			try (InputStream is = new FileInputStream(backup)) {
-				backupController.performImport(is);
+				backupController.performImport(is, new ProgressHolder());
 				long newGroupCount = groupDao.count();
 				long newUserCount = userDao.count();
 				long newRoomCount = roomDao.count();