This is an automated email from the ASF dual-hosted git repository. solomax pushed a commit to branch OPENMEETINGS-2402-reworked-upload in repository https://gitbox.apache.org/repos/asf/openmeetings.git
commit 4218027954b26053145b40511678d0f69185afdc Author: Maxim Solodovnik <solomax...@gmail.com> AuthorDate: Mon Jul 27 20:31:09 2020 +0700 [OPENMEETINGS-2402] file upload is re-worked (incomplete) --- .../openmeetings/core/remote/BaseMockedTest.java | 4 +- .../openmeetings/db/entity/file/FileItemTest.java | 4 +- .../apache/openmeetings/web/app/Application.java | 2 + .../openmeetings/web/common/OmWebSocketPanel.java | 23 +-- .../org/apache/openmeetings/web/common/main.js | 4 +- .../sidebar/RoomFileUploadResourceReference.java | 218 ++++++++++++++++++++ .../web/room/sidebar/UploadDialog.html | 19 +- .../web/room/sidebar/UploadDialog.java | 222 ++------------------- .../apache/openmeetings/web/room/sidebar/upload.js | 64 +++++- .../web/user/record/RecordingsPanel.java | 7 +- 10 files changed, 314 insertions(+), 253 deletions(-) diff --git a/openmeetings-core/src/test/java/org/apache/openmeetings/core/remote/BaseMockedTest.java b/openmeetings-core/src/test/java/org/apache/openmeetings/core/remote/BaseMockedTest.java index 731a95b..f8f4b6b 100644 --- a/openmeetings-core/src/test/java/org/apache/openmeetings/core/remote/BaseMockedTest.java +++ b/openmeetings-core/src/test/java/org/apache/openmeetings/core/remote/BaseMockedTest.java @@ -23,6 +23,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; import static org.powermock.api.mockito.PowerMockito.mockStatic; import org.apache.openmeetings.core.util.WebSocketHelper; @@ -35,7 +36,6 @@ import org.kurento.client.internal.TransactionImpl; import org.kurento.client.internal.client.RomManager; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @@ -65,7 +65,7 @@ public class BaseMockedTest { @Before public void setup() { - MockitoAnnotations.initMocks(this); + initMocks(this); mockStatic(KurentoClient.class); mockStatic(WebSocketHelper.class); doReturn(kServerManager).when(client).getServerManager(); diff --git a/openmeetings-db/src/test/java/org/apache/openmeetings/db/entity/file/FileItemTest.java b/openmeetings-db/src/test/java/org/apache/openmeetings/db/entity/file/FileItemTest.java index 562f37e..11e5156 100644 --- a/openmeetings-db/src/test/java/org/apache/openmeetings/db/entity/file/FileItemTest.java +++ b/openmeetings-db/src/test/java/org/apache/openmeetings/db/entity/file/FileItemTest.java @@ -21,6 +21,7 @@ package org.apache.openmeetings.db.entity.file; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; import static org.powermock.api.mockito.PowerMockito.mockStatic; import java.io.File; @@ -31,7 +32,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @@ -47,7 +47,7 @@ public class FileItemTest { @Before public void setup() { - MockitoAnnotations.initMocks(this); + initMocks(this); // Setup path to be local test resources mockStatic(OmFileHelper.class); diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java index 2554e84..be17a30 100644 --- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java +++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java @@ -78,6 +78,7 @@ import org.apache.openmeetings.web.pages.install.InstallWizardPage; import org.apache.openmeetings.web.room.GroupCustomCssResourceReference; import org.apache.openmeetings.web.room.RoomPreviewResourceReference; import org.apache.openmeetings.web.room.RoomResourceReference; +import org.apache.openmeetings.web.room.sidebar.RoomFileUploadResourceReference; import org.apache.openmeetings.web.room.wb.WbWebSocketHelper; import org.apache.openmeetings.web.user.dashboard.MyRoomsWidgetDescriptor; import org.apache.openmeetings.web.user.dashboard.RecentRoomsWidgetDescriptor; @@ -321,6 +322,7 @@ public class Application extends AuthenticatedWebApplication implements IApplica mountResource("/recordings/png/${id}", new PngRecordingResourceReference()); //should be in sync with VideoPlayer mountResource("/room/file/${id}", new RoomResourceReference()); mountResource("/room/preview/${id}", new RoomPreviewResourceReference()); + mountResource("/room/file/upload", new RoomFileUploadResourceReference()); mountResource("/profile/${id}", new ProfileImageResourceReference()); mountResource("/group/${id}", new GroupLogoResourceReference()); mountResource("/group/customcss/${id}", new GroupCustomCssResourceReference()); diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/OmWebSocketPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/OmWebSocketPanel.java index 3029726..2b758cc 100644 --- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/OmWebSocketPanel.java +++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/OmWebSocketPanel.java @@ -29,8 +29,7 @@ import org.apache.openmeetings.core.util.WebSocketHelper; import org.apache.openmeetings.db.entity.basic.Client; import org.apache.openmeetings.db.entity.basic.IWsClient; import org.apache.wicket.Component; -import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior; -import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.behavior.Behavior; import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.head.JavaScriptHeaderItem; import org.apache.wicket.markup.head.OnDomReadyHeaderItem; @@ -66,7 +65,7 @@ public abstract class OmWebSocketPanel extends Panel { @Override protected void onInitialize() { super.onInitialize(); - add(newWsBehavior(), new AbstractDefaultAjaxBehavior() { + add(newWsBehavior(), new Behavior() { private static final long serialVersionUID = 1L; @Override @@ -75,20 +74,9 @@ public abstract class OmWebSocketPanel extends Panel { log.debug("pingTimer is attached"); pingable = true; super.renderHead(component, response); - response.render(OnDomReadyHeaderItem.forScript(getJs())); + response.render(OnDomReadyHeaderItem.forScript("OmUtil.ping();")); } } - - private CharSequence getJs() { - return "OmUtil.ping(function(){" + getCallbackScript() + "});"; - } - - @Override - protected void respond(AjaxRequestTarget target) { - log.debug("Sending WebSocket PING"); - target.appendJavaScript(getJs()); - WebSocketHelper.sendClient(getWsClient(), new byte[]{getUserId() == null ? 0 : getUserId().byteValue()}); - } }); } @@ -137,6 +125,11 @@ public abstract class OmWebSocketPanel extends Panel { WebSocketHelper.sendRoomOthers(c.getRoomId(), c.getUid(), m.put("uid", c.getUid())); } break; + case "ping": + log.debug("Sending WebSocket PING"); + handler.appendJavaScript("OmUtil.ping();"); + WebSocketHelper.sendClient(getWsClient(), new byte[]{getUserId() == null ? 0 : getUserId().byteValue()}); + break; default: OmWebSocketPanel.this.onMessage(handler, m); } diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/main.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/main.js index 08a261f..aa453a7 100644 --- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/main.js +++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/main.js @@ -122,8 +122,8 @@ var OmUtil = (function() { self.setCssVar = function(key, val) { ($('body')[0]).style.setProperty(key, val); }; - self.ping = function(callback) { - setTimeout(callback, 30000); + self.ping = function() { + setTimeout(() => OmUtil.sendMessage({type: 'ping'}), 30000); } ; return self; })(); 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 new file mode 100644 index 0000000..1c9c94a --- /dev/null +++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomFileUploadResourceReference.java @@ -0,0 +1,218 @@ +/* + * 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.room.sidebar; + +import static org.apache.openmeetings.util.OpenmeetingsVariables.getMaxUploadSize; +import static org.apache.openmeetings.web.app.WebSession.getUserId; +import static org.apache.openmeetings.web.util.ThreadHelper.startRunnable; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.DoubleConsumer; + +import javax.ws.rs.core.MediaType; + +import org.apache.commons.fileupload.FileItem; +import org.apache.openmeetings.core.data.file.FileProcessor; +import org.apache.openmeetings.core.util.WebSocketHelper; +import org.apache.openmeetings.db.dao.file.FileItemDao; +import org.apache.openmeetings.db.dao.file.FileItemLogDao; +import org.apache.openmeetings.db.entity.basic.Client; +import org.apache.openmeetings.db.entity.file.BaseFileItem; +import org.apache.openmeetings.db.entity.room.Room; +import org.apache.openmeetings.db.entity.room.Room.Right; +import org.apache.openmeetings.db.entity.room.Room.RoomElement; +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.ClientManager; +import org.apache.wicket.injection.Injector; +import org.apache.wicket.protocol.http.servlet.MultipartServletWebRequest; +import org.apache.wicket.protocol.http.servlet.ServletWebRequest; +import org.apache.wicket.request.resource.AbstractResource; +import org.apache.wicket.request.resource.AbstractResource.ResourceResponse; +import org.apache.wicket.request.resource.AbstractResource.WriteCallback; +import org.apache.wicket.request.resource.IResource; +import org.apache.wicket.request.resource.IResource.Attributes; +import org.apache.wicket.request.resource.ResourceReference; +import org.apache.wicket.spring.injection.annot.SpringBean; +import org.apache.wicket.util.lang.Bytes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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_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"; + private static final String PARAM_LAST_SELECTED_NAME = "room-upload-last-selected"; + private static enum Status { + SUCCESS + , PROGRESS + , ERROR + } + + @SpringBean + private ClientManager cm; + @SpringBean + private FileProcessor processor; + @SpringBean + private FileItemLogDao fileLogDao; + @SpringBean + private FileItemDao fileDao; + + public RoomFileUploadResourceReference() { + super(RoomFileUploadResourceReference.class, "room-file-upload"); + Injector.get().inject(this); + } + + @Override + public IResource getResource() { + return new AbstractResource() { + private static final long serialVersionUID = 1L; + + @Override + protected ResourceResponse newResourceResponse(Attributes attributes) { + final ResourceResponse response = new ResourceResponse(); + final ServletWebRequest webRequest = (ServletWebRequest) attributes.getRequest(); + try { + MultipartServletWebRequest multiPartRequest = webRequest.newMultipartWebRequest(Bytes.bytes(getMaxUploadSize()), "ignored"); + multiPartRequest.parseFileParts(); + + String sid = multiPartRequest.getPostParameters().getParameterValue(PARAM_SID_NAME).toString(); + Client c = cm.getBySid(sid); + final long langId = getLangId(c); + if (isUploadAllowed(c)) { + Map<String, List<FileItem>> files = multiPartRequest.getFiles(); + final List<FileItem> fileItems = files.get(PARAM_FILE_NAME); + + final boolean toWb = multiPartRequest.getPostParameters().getParameterValue(PARAM_TO_WB_NAME).toBoolean(false); + final boolean clean = multiPartRequest.getPostParameters().getParameterValue(PARAM_CLEAN_NAME).toBoolean(false); + final long lastSelected = multiPartRequest.getPostParameters().getParameterValue(PARAM_LAST_SELECTED_NAME).toLong(-1L); + startRunnable(() -> convertAll(c, fileItems, toWb, clean, lastSelected)); + + prepareResponse(response, Status.SUCCESS, Application.getString("54", langId)); + } else { + prepareResponse(response, Status.ERROR, Application.getString("access.denied.header", langId)); + } + } catch (Exception e) { + log.error("An error occurred while uploading a file", e); + prepareResponse(response, Status.ERROR, e.getMessage()); + } + return response; + } + }; + } + + private static long getLangId(Client c) { + return c == null || c.getUser() == null ? 1L : c.getUser().getLanguageId(); + } + + private static void prepareResponse(ResourceResponse response, Status status, String msg) { + response.setContentType(MediaType.APPLICATION_JSON); + response.setWriteCallback(new WriteCallback() { + @Override + public void writeData(Attributes attributes) throws IOException { + attributes.getResponse().write(new JSONObject() + .put("status", status.name()) + .put("message", msg) + .toString()); + } + }); + } + + private static boolean isUploadAllowed(Client c) { + if (c == null || c.getRoom() == null || Room.Type.INTERVIEW == c.getRoom().getType()) { + return false; + } + Room r = c.getRoom(); + return !r.isHidden(RoomElement.FILES) && c.hasRight(Right.PRESENTER); + } + + private void convertAll(Client c, List<FileItem> files, boolean toWb, boolean clean, long lastSelected) { + final BaseFileItem parent = fileDao.get(lastSelected); + final long langId = getLangId(c); + final long totalSize = files.stream().mapToLong(FileItem::getSize).sum(); + final AtomicInteger progress = new AtomicInteger(0); + long currentSize = 0; + for (FileItem curItem : files) { + long size = curItem.getSize(); + try { + org.apache.openmeetings.db.entity.file.FileItem f = new org.apache.openmeetings.db.entity.file.FileItem(); + f.setSize(size); + f.setName(curItem.getName()); + if (parent == null || BaseFileItem.Type.RECORDING == parent.getType()) { + f.setOwnerId(getUserId()); + } else { + f.setRoomId(parent.getRoomId()); + f.setOwnerId(parent.getOwnerId()); + f.setGroupId(parent.getGroupId()); + if (parent.getId() != null) { + f.setParentId(BaseFileItem.Type.FOLDER == parent.getType() ? parent.getId() : parent.getParentId()); + } + } + f.setInsertedBy(getUserId()); + + ProcessResultList logs = processor.processFile(f, curItem.getInputStream() + , Optional.<DoubleConsumer>of(part -> sendProgress(c, progress, progress.get() + (int)(100 * part * size / totalSize)))); + for (ProcessResult res : logs.getJobs()) { + fileLogDao.add(res.getProcess(), f, res); + } + if (logs.hasError()) { + sendError(c, Application.getString("convert.errors.file", langId)); + } else { + if (toWb) { + //FIXME TODO room.getWb().sendFileToWb(f, clean); + clean = false; + } + } + } catch (Exception e) { + log.error("Unexpected error while processing uploaded file", e); + sendError(c, e.getMessage() == null ? "Unexpected error" : e.getMessage()); + } finally { + curItem.delete(); + } + currentSize += size; + sendProgress(c, progress, (int)(100 * currentSize / totalSize)); + } + sendProgress(c, progress, 100); + } + + private void sendError(Client c, String msg) { + WebSocketHelper.sendClient(c, new JSONObject() + .put("status", Status.ERROR.name()) + .put("message", msg)); + } + + private void sendProgress(Client c, AtomicInteger progress, int cur) { + if (cur > progress.get()) { + progress.set(cur); + WebSocketHelper.sendClient(c, new JSONObject() + .put("status", Status.PROGRESS.name()) + .put("progress", cur)); + } + } +} 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 181cf1d..81c9a4e 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,26 +21,25 @@ <!DOCTYPE html> <html xmlns:wicket="http://wicket.apache.org"> <wicket:extend> - <form wicket:id="form"> + <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" wicket:id="file"/> + <input type="file" multiple="multiple" id="room-upload-file" name="room-upload-file"/> </span> </div> - <div wicket:id="feedback" class="error"></div> + <div class="error"></div> <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"> - <input wicket:id="clean-wb" type="checkbox"/><label wicket:for="clean-wb"><wicket:message key="62"/></label> + <input id="room-upload-to-wb" name="room-upload-to-wb" type="checkbox" value="true"/> <label for="room-upload-to-wb"><wicket:message key="1312"/></label> + <div class="ml-3 d-none" id="room-upload-clean-block"> + <input id="room-upload-clean" name="room-upload-clean" type="checkbox" value="true"/> <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="lastSelected" name="room-upload-last-selected" type="hidden" value=""/> </form> - <div wicket:id="progress"></div> - <form wicket:id="name-form"> - <input type="hidden" wicket:id="name"/> - </form> + <div id="progress"></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 81c57a8..1651cc8 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 @@ -19,133 +19,42 @@ package org.apache.openmeetings.web.room.sidebar; import static org.apache.openmeetings.util.OpenmeetingsVariables.getMaxUploadSize; -import static org.apache.openmeetings.web.app.WebSession.getUserId; - -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.function.DoubleConsumer; import org.apache.openmeetings.core.data.file.FileProcessor; import org.apache.openmeetings.db.dao.file.FileItemLogDao; -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.WebSession; import org.apache.openmeetings.web.common.OmModalCloseButton; 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.ajax.AjaxRequestTarget; -import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior; -import org.apache.wicket.ajax.form.OnChangeAjaxBehavior; +import org.apache.wicket.behavior.AttributeAppender; 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.form.CheckBox; -import org.apache.wicket.markup.html.form.Form; -import org.apache.wicket.markup.html.form.HiddenField; -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.mapper.parameter.PageParameters; import org.apache.wicket.request.resource.JavaScriptResourceReference; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.util.lang.Bytes; -import org.apache.wicket.util.string.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -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.utilities.BackgroundColorBehavior; -import de.agilecoders.wicket.extensions.markup.html.bootstrap.spinner.SpinnerAjaxButton; 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"); - private final Form<String> form = new Form<>("form") { - private static final long serialVersionUID = 1L; - - @Override - protected boolean handleMultiPart() { - try { - return super.handleMultiPart(); - } catch (Exception e) { - log.warn("Multipart processing failed {}", e.getMessage()); - } - return true; - } - }; - private SpinnerAjaxButton upload; - private final FileUploadField uploadField = new FileUploadField("file", 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 final HiddenField<String> fileName = new HiddenField<>("name", Model.of("")); - private final CheckBox toWb = new CheckBox("to-wb", Model.of(false)); - private final WebMarkupContainer cleanBlock = new WebMarkupContainer("clean-block"); - private final CheckBox cleanWb = new CheckBox("clean-wb", Model.of(false)); + private final WebMarkupContainer form = new WebMarkupContainer("form"); private final RoomFilePanel roomFiles; private final RoomPanel room; + private final WebMarkupContainer lastSelected = new WebMarkupContainer("lastSelected"); @SpringBean private FileProcessor processor; @SpringBean private FileItemLogDao fileLogDao; - private final UpdatableProgressBar progressBar = new UpdatableProgressBar("progress", new Model<>(0), BackgroundColorBehavior.Color.Info, true) { - private static final long serialVersionUID = 1L; - - @Override - protected IModel<Integer> newValue() { - return Model.of(progress); - } - - @Override - protected void onPostProcessTarget(IPartialPageRequestHandler target) { - super.onPostProcessTarget(target); - target.appendJavaScript("Ladda.create(document.getElementById('" + upload.getMarkupId() + "')).start()"); - } - - @Override - 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 int progress = 0; - public UploadDialog(String id, RoomPanel room, RoomFilePanel roomFiles) { super(id); this.roomFiles = roomFiles; @@ -158,73 +67,26 @@ public class UploadDialog extends Modal<String> { setCloseOnEscapeKey(false); setBackdrop(Backdrop.STATIC); - add(form.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true)); - toWb.add(new OnChangeAjaxBehavior() { - private static final long serialVersionUID = 1L; - - @Override - protected void onUpdate(AjaxRequestTarget target) { - target.add(cleanBlock.setVisible(toWb.getModelObject())); - } - }); - form.add(feedback.setOutputMarkupId(true), toWb.setOutputMarkupId(true) - , cleanBlock.add(cleanWb.setOutputMarkupId(true)).setVisible(false).setOutputMarkupPlaceholderTag(true)); - - form.setMultiPart(true); - form.setMaxSize(Bytes.bytes(getMaxUploadSize())); - // Model is necessary here to avoid writing image to the User object - form.add(uploadField); - final Form<String> nameForm = new Form<>("name-form"); - fileName.add(new AjaxFormSubmitBehavior(nameForm, "change") { - private static final long serialVersionUID = 1L; - - @Override - protected void onSubmit(AjaxRequestTarget target) { - if (!Strings.isEmpty(getComponent().getDefaultModelObjectAsString())) { - target.add(upload.setEnabled(true)); - } - } - }).setOutputMarkupId(true); - - add(nameForm.add(fileName.setOutputMarkupId(true))); + add(form.add(AttributeAppender.append("data-max-size", getMaxUploadSize())) + .add(AttributeAppender.append("data-max-size-lbl", Bytes.bytes(getMaxUploadSize()).toString(WebSession.get().getLocale()))) + .add(AttributeAppender.append("data-upload-lbl", getString("593"))) + .add(AttributeAppender.append("data-max-upload-lbl", getString("1491"))) + .add(AttributeAppender.append("action", "" + urlFor(new RoomFileUploadResourceReference(), new PageParameters()))) + .setOutputMarkupId(true) + .setOutputMarkupPlaceholderTag(true)); + form.add(new WebMarkupContainer("sid").add(AttributeAppender.append("value", room.getClient().getSid())).setMarkupId("room-upload-sid").setOutputMarkupId(true)); + form.add(lastSelected.setMarkupId("room-upload-last-selected").setOutputMarkupId(true)); add(BootstrapFileUploadBehavior.INSTANCE); - addButton(upload = new SpinnerAjaxButton("button", new ResourceModel("593"), form, Buttons.Type.Outline_Primary) { - private static final long serialVersionUID = 1L; - - @Override - protected void onError(AjaxRequestTarget target) { - target.add(feedback); - } - - @Override - protected void onSubmit(AjaxRequestTarget target) { - List<FileUpload> ful = uploadField.getFileUploads(); - if (ful != null) { - - progress = 0; - progressBar.restart(target); - target.add( - progressBar.setModelObject(progress).setVisible(true) - , form.setVisible(false)); - - ThreadHelper.startRunnable(UploadDialog.this::convertAll); - } - } - }); - upload.setEnabled(false); addButton(OmModalCloseButton.of("85")); - progressBar.updateInterval(Duration.ofSeconds(1)).stop(null).striped(false); - add(progressBar.setOutputMarkupPlaceholderTag(true).setVisible(false)); super.onInitialize(); } @Override 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())); + lastSelected.add(AttributeAppender.replace("value", roomFiles.getLastSelected().getId())); + handler.add(form.setVisible(true)); + handler.appendJavaScript(String.format("bindUpload();")); return super.show(handler); } @@ -233,54 +95,4 @@ public class UploadDialog extends Modal<String> { super.renderHead(response); response.render(new PriorityHeaderItem(JavaScriptHeaderItem.forReference(new JavaScriptResourceReference(UploadDialog.class, "upload.js")))); } - - private void convertAll() { - List<FileUpload> ful = uploadField.getFileUploads(); - final BaseFileItem parent = roomFiles.getLastSelected(); - boolean clean = cleanWb.getModelObject(); - final long totalSize = ful.stream().mapToLong(FileUpload::getSize).sum(); - long currentSize = 0; - for (FileUpload fu : ful) { - long size = fu.getSize(); - try { - FileItem f = new FileItem(); - f.setSize(size); - f.setName(fu.getClientFileName()); - if (parent == null || !(parent instanceof FileItem)) { - f.setOwnerId(getUserId()); - } else { - f.setRoomId(parent.getRoomId()); - f.setOwnerId(parent.getOwnerId()); - f.setGroupId(parent.getGroupId()); - if (parent.getId() != null) { - f.setParentId(BaseFileItem.Type.FOLDER == parent.getType() ? parent.getId() : parent.getParentId()); - } - } - f.setInsertedBy(getUserId()); - - ProcessResultList logs = processor.processFile(f, fu.getInputStream() - , Optional.<DoubleConsumer>of(part -> progress += (int)(100 * part * size / totalSize))); - for (ProcessResult res : logs.getJobs()) { - fileLogDao.add(res.getProcess(), f, res); - } - if (logs.hasError()) { - form.error(getString("convert.errors.file")); - } else { - if (toWb.getModelObject()) { - room.getWb().sendFileToWb(f, clean); - clean = false; - } - } - } catch (Exception e) { - log.error("Unexpected error while processing uploaded file", e); - form.error(e.getMessage() == null ? "Unexpected error" : e.getMessage()); - } finally { - fu.closeStreams(); - fu.delete(); - } - currentSize += size; - progress = (int)(100 * currentSize / totalSize); - } - progress = 100; - } } diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/upload.js b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/upload.js index df0d0a8..0791978 100644 --- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/upload.js +++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/upload.js @@ -1,21 +1,63 @@ /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */ -function bindUpload(markupId, hiddenId) { - const fi = $('#' + markupId + ' .fileinput'); +function bindUpload() { + const form = $('#room-upload-form') + , fi = form.find('.fileinput') + 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() { + $.ajax({ + url: form.attr('action') + , type: 'POST' + , data: new FormData($('#room-upload-form')[0]) + , processData: false + , contentType: false + }).done(function(data) { + let i = 0; + }).fail(function(e) { + let i = 0; + }); + }); + } + uploadBtn.attr('disabled', 'disabled'); if (!fi.eventAdded) { - $('#' + markupId + ' .fileinput').on('change.bs.fileinput', function(event) { + fi.on('change.bs.fileinput', function(event) { event.stopPropagation(); - const th = $(this), - fInput = th.find('input[type=file]'), - fn = th.find('.fileinput-filename'); - if (fInput[0].files !== undefined && fInput[0].files.length > 1) { - fn.text($.map(fInput[0].files, function(val) { return val.name; }).join(', ')); + 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()); - const hi = $('#' + hiddenId); - hi.val(fn.text()); - hi.trigger('change'); 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; } } diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/record/RecordingsPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/record/RecordingsPanel.java index 6c80fa9..aec9bb7 100644 --- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/record/RecordingsPanel.java +++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/record/RecordingsPanel.java @@ -127,12 +127,7 @@ public class RecordingsPanel extends UserBasePanel { @Override public void onClick(AjaxRequestTarget target) { final IRecordingConverter converter = isInterview ? interviewConverter : recordingConverter; - new Thread() { - @Override - public void run() { - converter.startConversion((Recording)getLastSelected()); - } - }.start(); + new Thread(() -> converter.startConversion((Recording)getLastSelected())).start(); } }, new BootstrapAjaxLink<>(markupId, Model.of(""), Buttons.Type.Outline_Success, new ResourceModel("button.label.share")) { private static final long serialVersionUID = 1L;