From 6df1e934ee178499a74d510d22ed3eedc44d7e79 Mon Sep 17 00:00:00 2001 From: AnbazhakanSubramaniam <49298739+AnbazhakanSubramaniam@users.noreply.github.com> Date: Thu, 9 Apr 2020 18:21:55 +0530 Subject: [PATCH] Allow to download an artifact from the UI (#786) (#944) This feature allows to download the artifact from the Artifact details table Review points are handled Signed-off-by: usb1cob --- .../details/ArtifactDetailsLayout.java | 78 ++++++++++++++++--- .../ui/utils/UIComponentIdProvider.java | 5 ++ .../hawkbit/ui/utils/UIMessageIdProvider.java | 4 + .../src/main/resources/messages.properties | 3 +- 4 files changed, 79 insertions(+), 11 deletions(-) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java index 1b5cb3c8f..b613f6b24 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/artifacts/details/ArtifactDetailsLayout.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.ui.artifacts.details; +import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -16,8 +17,10 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.SoftwareModuleManagement; +import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.ui.artifacts.event.ArtifactDetailsEvent; import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent; @@ -46,7 +49,10 @@ import org.vaadin.spring.events.annotation.EventBusListenerMethod; import com.google.common.collect.Maps; import com.vaadin.data.Container; +import com.vaadin.server.ErrorHandler; +import com.vaadin.server.FileDownloader; import com.vaadin.server.FontAwesome; +import com.vaadin.server.StreamResource; import com.vaadin.ui.Alignment; import com.vaadin.ui.Button; import com.vaadin.ui.HorizontalLayout; @@ -290,19 +296,71 @@ public class ArtifactDetailsLayout extends VerticalLayout { private static final long serialVersionUID = 1L; @Override - public Button generateCell(final Table source, final Object itemId, final Object columnId) { - final String fileName = (String) table.getContainerDataSource().getItem(itemId) - .getItemProperty(PROVIDED_FILE_NAME).getValue(); - final Button deleteIcon = SPUIComponentProvider.getButton( - fileName + "-" + UIComponentIdProvider.UPLOAD_FILE_DELETE_ICON, "", - i18n.getMessage(UIMessageIdProvider.CAPTION_DISCARD), ValoTheme.BUTTON_TINY + " " + "blueicon", - true, FontAwesome.TRASH_O, SPUIButtonStyleNoBorder.class); - deleteIcon.setData(itemId); - deleteIcon.addClickListener(event -> confirmAndDeleteArtifact((Long) itemId, fileName)); - return deleteIcon; + public HorizontalLayout generateCell(final Table source, final Object itemId, final Object columnId) { + HorizontalLayout actioncellLayout = new HorizontalLayout(); + actioncellLayout.addComponent(getArtifactDeleteButton(table, itemId)); + actioncellLayout.addComponent(getArtifactDownloadButton(table, itemId)); + actioncellLayout.setImmediate(true); + return actioncellLayout; + } + }); + } + + private Button getArtifactDeleteButton(final Table table, final Object itemId) { + final String fileName = (String) table.getContainerDataSource().getItem(itemId) + .getItemProperty(PROVIDED_FILE_NAME).getValue(); + final Button deleteIcon = SPUIComponentProvider.getButton( + fileName + "-" + UIComponentIdProvider.UPLOAD_FILE_DELETE_ICON, "", + i18n.getMessage(UIMessageIdProvider.CAPTION_DISCARD), ValoTheme.BUTTON_TINY + " " + "blueicon", + true, FontAwesome.TRASH_O, SPUIButtonStyleNoBorder.class); + deleteIcon.setData(itemId); + deleteIcon.addClickListener(event -> confirmAndDeleteArtifact((Long) itemId, fileName)); + return deleteIcon; + } + + private Button getArtifactDownloadButton(final Table table, final Object itemId) { + final String fileName = (String) table.getContainerDataSource().getItem(itemId) + .getItemProperty(PROVIDED_FILE_NAME).getValue(); + final Button downloadIcon = SPUIComponentProvider.getButton( + fileName + "-" + UIComponentIdProvider.ARTIFACT_FILE_DOWNLOAD_ICON, "", + i18n.getMessage(UIMessageIdProvider.TOOLTIP_ARTIFACT_DOWNLOAD), ValoTheme.BUTTON_TINY + " " + "blueicon", + true, FontAwesome.DOWNLOAD, SPUIButtonStyleNoBorder.class); + downloadIcon.setData(itemId); + FileDownloader fileDownloader = new FileDownloader(createStreamResource((Long) itemId)); + fileDownloader.extend(downloadIcon); + fileDownloader.setErrorHandler(new ErrorHandler() { + + /** + * Error handler for file downloader + */ + private static final long serialVersionUID = 4030230501114422570L; + + @Override + public void error(com.vaadin.server.ErrorEvent event) { + uINotification.displayValidationError(i18n.getMessage(UIMessageIdProvider.ARTIFACT_DOWNLOAD_FAILURE_MSG)); } }); + return downloadIcon; + } + private StreamResource createStreamResource(final Long id) { + + Optional artifact = this.artifactManagement.get(id); + if (artifact.isPresent()) { + Optional file = artifactManagement.loadArtifactBinary(artifact.get().getSha1Hash()); + return new StreamResource(new StreamResource.StreamSource() { + private static final long serialVersionUID = 1L; + + @Override + public InputStream getStream() { + if (file.isPresent()) { + return file.get().getFileInputStream(); + } + return null; + } + }, artifact.get().getFilename()); + } + return null; } private void confirmAndDeleteArtifact(final Long id, final String fileName) { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java index 32c89a974..d8e8ee2e8 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java @@ -387,6 +387,11 @@ public final class UIComponentIdProvider { */ public static final String UPLOAD_SOFTWARE_MODULE_TABLE = "upload.swModule.table"; + /** + * Artifact - file download button id + */ + public static final String ARTIFACT_FILE_DOWNLOAD_ICON = "artifact.file.download.button"; + /** * Upload result popup close button. */ diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIMessageIdProvider.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIMessageIdProvider.java index a810877df..352e52b58 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIMessageIdProvider.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIMessageIdProvider.java @@ -15,6 +15,10 @@ package org.eclipse.hawkbit.ui.utils; */ public final class UIMessageIdProvider { + public static final String TOOLTIP_ARTIFACT_DOWNLOAD = "tooltip.artifact.download"; + + public static final String ARTIFACT_DOWNLOAD_FAILURE_MSG = "message.artifact.download.failure"; + public static final String BUTTON_CANCEL = "button.cancel"; public static final String BUTTON_OK = "button.ok"; diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index 354aa11fe..3a89fdaf1 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -304,6 +304,7 @@ tooltip.next.maintenance.window = next on {0} tooltip.target.attributes.update.request = Request attributes update tooltip.target.attributes.update.requested = Update already requested tooltip.documentation.link=Documentation +tooltip.artifact.download=Download Artifact #rollout action tooltip.rollout.run = Run @@ -472,7 +473,7 @@ message.upload.fileSizeQuota = Maximum artifact size ({0}) exceeded message.upload.storageQuota = Storage quota exceeded, {0} left message.uploadedfile.illegalFilename = Filename contains illegal characters message.artifact.deleted = Artifact with file {0} deleted successfully - +message.artifact.download.failure = Artifact Download Failed artifact.upload.popup.caption = Upload status artifact.upload.status.caption = Status