diff --git a/ccm-cms/src/main/java/org/librecms/Cms.java b/ccm-cms/src/main/java/org/librecms/Cms.java
index edfc313f6..40764489b 100644
--- a/ccm-cms/src/main/java/org/librecms/Cms.java
+++ b/ccm-cms/src/main/java/org/librecms/Cms.java
@@ -96,7 +96,8 @@ import java.util.Properties;
)
},
configurations = {
- org.librecms.CMSConfig.class
+ org.librecms.CMSConfig.class,
+ org.librecms.assets.BinaryAssetConfig.class
},
pageModelComponentModels = {
@PageModelComponentModel(
diff --git a/ccm-cms/src/main/java/org/librecms/assets/BinaryAssetBlobDataProvider.java b/ccm-cms/src/main/java/org/librecms/assets/BinaryAssetBlobDataProvider.java
new file mode 100644
index 000000000..2f12c6059
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/assets/BinaryAssetBlobDataProvider.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.assets;
+
+import org.libreccm.core.UnexpectedErrorException;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.sql.Blob;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Objects;
+
+import javax.annotation.Resource;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.Dependent;
+import javax.sql.DataSource;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@Dependent
+public class BinaryAssetBlobDataProvider implements BinaryAssetDataProvider {
+
+ @Resource(lookup = "java:/comp/env/jdbc/libreccm/db")
+ private DataSource dataSource;
+
+ @Override
+ public InputStream retrieveData(final BinaryAsset asset) {
+ Objects.requireNonNull(asset, "Can't retrieve data from null.");
+ try ( Connection connection = dataSource.getConnection()) {
+ final PreparedStatement stmt = connection
+ .prepareStatement(
+ "SELECT data FROM binary_assets WHERE object_id = ?"
+ );
+ stmt.setLong(1, asset.getObjectId());
+
+ try (ResultSet resultSet = stmt.executeQuery()) {
+ final Blob blob = resultSet.getBlob("data");
+ return blob.getBinaryStream();
+ }
+ } catch (SQLException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+ }
+
+ @Override
+ public void saveData(final BinaryAsset asset, final InputStream stream) {
+ Objects.requireNonNull(asset, "Can't save data to null.");
+ try ( Connection connection = dataSource.getConnection()) {
+ final PreparedStatement stmt = connection
+ .prepareStatement(
+ "UPDATE binary_assets SET data = ? WHERE object_id = ?"
+ );
+ stmt.setBlob(1, stream);
+ stmt.setLong(2, asset.getObjectId());
+ } catch (SQLException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/assets/BinaryAssetConfig.java b/ccm-cms/src/main/java/org/librecms/assets/BinaryAssetConfig.java
new file mode 100644
index 000000000..b8d5683ae
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/assets/BinaryAssetConfig.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.assets;
+
+import org.libreccm.configuration.Configuration;
+import org.libreccm.configuration.Setting;
+
+import java.util.Objects;
+
+/**
+ * Configuration parameters for binary assets.
+ *
+ * @author Jens Pelzetter
+ */
+@Configuration
+public final class BinaryAssetConfig {
+
+ /**
+ * Sets the implementation of {@link BinaryAssetDataProvider} to use.
+ */
+ @Setting
+ private String binaryAssetDataProvider = BinaryAssetBlobDataProvider.class
+ .getName();
+
+ public String getBinaryAssetDataProvider() {
+ return binaryAssetDataProvider;
+ }
+
+ public void setBinaryAssetDataProvider(final String binaryAssetDataProvider) {
+ this.binaryAssetDataProvider = binaryAssetDataProvider;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 3;
+ hash = 67 * hash + Objects.hashCode(binaryAssetDataProvider);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof BinaryAssetConfig)) {
+ return false;
+ }
+ final BinaryAssetConfig other = (BinaryAssetConfig) obj;
+ return Objects.equals(
+ binaryAssetDataProvider,
+ other.binaryAssetDataProvider
+ );
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "%s{ "
+ + "binaryAssetDataProvider = %s"
+ + " }",
+ binaryAssetDataProvider
+ );
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/assets/BinaryAssetDataProvider.java b/ccm-cms/src/main/java/org/librecms/assets/BinaryAssetDataProvider.java
new file mode 100644
index 000000000..ba5b4c8c1
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/assets/BinaryAssetDataProvider.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.assets;
+
+import java.io.InputStream;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public interface BinaryAssetDataProvider {
+
+ InputStream retrieveData(BinaryAsset asset);
+
+ void saveData(BinaryAsset asset, InputStream stream);
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/assets/BinaryAssetDataService.java b/ccm-cms/src/main/java/org/librecms/assets/BinaryAssetDataService.java
new file mode 100644
index 000000000..c7490ba42
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/assets/BinaryAssetDataService.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.assets;
+
+import org.libreccm.configuration.ConfigurationManager;
+import org.libreccm.core.UnexpectedErrorException;
+
+import java.io.InputStream;
+import java.util.Objects;
+
+import javax.enterprise.context.RequestScoped;
+import javax.enterprise.inject.Any;
+import javax.enterprise.inject.Instance;
+import javax.inject.Inject;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class BinaryAssetDataService {
+
+ @Inject
+ private ConfigurationManager confManager;
+
+ @Inject
+ @Any
+ private Instance dataProvider;
+
+ public InputStream retrieveData(final BinaryAsset asset) {
+ Objects.requireNonNull(asset, "Can't retrieve data from null.");
+
+ final BinaryAssetDataProvider dataProvider = getDataProvider();
+
+ return dataProvider.retrieveData(asset);
+ }
+
+ public void saveData(final BinaryAsset asset, final InputStream stream) {
+ Objects.requireNonNull(asset, "Can't save data to null.");
+
+ final BinaryAssetDataProvider dataProvider = getDataProvider();
+ dataProvider.saveData(asset, stream);
+ }
+
+ @SuppressWarnings("unchecked")
+ private BinaryAssetDataProvider getDataProvider() {
+ final BinaryAssetConfig config = confManager.findConfiguration(
+ BinaryAssetConfig.class
+ );
+
+ final Class extends BinaryAssetDataProvider> clazz;
+ try {
+ clazz = (Class extends BinaryAssetDataProvider>) Class.forName(
+ config.getBinaryAssetDataProvider()
+ );
+ } catch (ClassNotFoundException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ final Instance extends BinaryAssetDataProvider> selectedInstance
+ = dataProvider.select(clazz);
+
+ if (selectedInstance.isResolvable()) {
+ return selectedInstance.get();
+ } else {
+ throw new UnexpectedErrorException(
+ String.format(
+ "The configured implementation of %s could not be resolved.",
+ BinaryAssetDataProvider.class.getName()
+ )
+ );
+ }
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/rs/Images.java b/ccm-cms/src/main/java/org/librecms/contentsection/rs/Images.java
index 9f0ca3c88..e1c2f6ca1 100644
--- a/ccm-cms/src/main/java/org/librecms/contentsection/rs/Images.java
+++ b/ccm-cms/src/main/java/org/librecms/contentsection/rs/Images.java
@@ -319,7 +319,6 @@ public class Images {
LOGGER.error(ex);
return Response.serverError().build();
}
-
return Response
.ok(outputStream.toByteArray(), mimeType)
.build();
diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/CmsAssetEditSteps.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/CmsAssetEditSteps.java
index 9aadc15d8..99c7c8e8b 100644
--- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/CmsAssetEditSteps.java
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/CmsAssetEditSteps.java
@@ -48,6 +48,7 @@ public class CmsAssetEditSteps implements MvcAssetEditSteps {
public Set> getResourceClasses() {
final Set> classes = new HashSet<>();
+ classes.add(FileAssetEditStepDownload.class);
classes.add(SideNoteEditStepResources.class);
return classes;
diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/FileAssetEditStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/FileAssetEditStep.java
index c0b8bb39c..97ad7b98d 100644
--- a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/FileAssetEditStep.java
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/FileAssetEditStep.java
@@ -24,12 +24,12 @@ import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.security.AuthorizationRequired;
+import org.librecms.assets.BinaryAssetDataService;
import org.librecms.assets.FileAsset;
import org.librecms.contentsection.AssetRepository;
import org.librecms.ui.contentsections.AssetPermissionsChecker;
import org.librecms.ui.contentsections.ContentSectionNotFoundException;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
@@ -55,6 +55,7 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
/**
*
@@ -84,6 +85,9 @@ public class FileAssetEditStep extends AbstractMvcAssetEditStep {
@Inject
private AssetRepository assetRepo;
+ @Inject
+ private BinaryAssetDataService dataService;
+
@Inject
private GlobalizationHelper globalizationHelper;
@@ -312,6 +316,9 @@ public class FileAssetEditStep extends AbstractMvcAssetEditStep {
}
}
+
+
+
@POST
@Path("/upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@@ -348,19 +355,10 @@ public class FileAssetEditStep extends AbstractMvcAssetEditStep {
.getHeaders();
fileName = getFileName(headers);
contentType = getContentType(headers);
- final byte[] bytes = new byte[1024];
- try (InputStream inputStream = inputPart.getBody(
- InputStream.class, null
+ dataService.saveData(
+ fileAsset,
+ inputPart.getBody(InputStream.class, null)
);
- ByteArrayOutputStream fileDataOutputStream
- = new ByteArrayOutputStream()) {
- while (inputStream.read(bytes) != -1) {
- fileDataOutputStream.writeBytes(bytes);
- }
-
- fileAsset.setData(fileDataOutputStream.toByteArray());
- }
-
} catch (IOException ex) {
LOGGER.error(
"Failed to upload file for FileAsset {}:", assetPath
@@ -370,6 +368,33 @@ public class FileAssetEditStep extends AbstractMvcAssetEditStep {
models.put("uploadFailed", true);
return buildRedirectPathForStep();
}
+
+// final MultivaluedMap headers = inputPart
+// .getHeaders();
+// fileName = getFileName(headers);
+// contentType = getContentType(headers);
+// final byte[] bytes = new byte[1024];
+// try (InputStream inputStream = inputPart.getBody(
+// InputStream.class, null
+// );
+// ByteArrayOutputStream fileDataOutputStream
+// = new ByteArrayOutputStream()) {
+// while (inputStream.read(bytes) != -1) {
+// fileDataOutputStream.writeBytes(bytes);
+// }
+//
+// fileAsset.setData(fileDataOutputStream.toByteArray());
+// }
+//
+// } catch (IOException ex) {
+// LOGGER.error(
+// "Failed to upload file for FileAsset {}:", assetPath
+// );
+// LOGGER.error(ex);
+//
+// models.put("uploadFailed", true);
+// return buildRedirectPathForStep();
+// }
}
fileAsset.setFileName(fileName);
diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/FileAssetEditStepDownload.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/FileAssetEditStepDownload.java
new file mode 100644
index 000000000..8c9438221
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/FileAssetEditStepDownload.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2021 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.librecms.ui.contentsections.assets;
+
+import org.libreccm.security.AuthorizationRequired;
+import org.librecms.assets.BinaryAssetDataService;
+import org.librecms.assets.FileAsset;
+import org.librecms.contentsection.Asset;
+import org.librecms.contentsection.AssetRepository;
+import org.librecms.contentsection.ContentSection;
+import org.librecms.ui.contentsections.ContentSectionNotFoundException;
+import org.librecms.ui.contentsections.ContentSectionsUi;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.mail.internet.ContentDisposition;
+import javax.transaction.Transactional;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.StreamingOutput;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Path(MvcAssetEditSteps.PATH_PREFIX + "fileasset-edit-download")
+
+public class FileAssetEditStepDownload {
+
+ @Inject
+ private AssetRepository assetRepo;
+
+ @Inject
+ private BinaryAssetDataService dataService;
+
+ @Inject
+ private ContentSectionsUi sectionsUi;
+
+ @GET
+ @Path("/")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public Response downloadFile(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath
+ ) {
+
+ final ContentSection contentSection = sectionsUi
+ .findContentSection(sectionIdentifier)
+ .orElseThrow(
+ () -> new WebApplicationException(
+ Response
+ .status(Response.Status.NOT_FOUND)
+ .entity(
+ String.format(
+ "ContentSection %s not found.",
+ sectionIdentifier
+ )
+ ).build()
+ )
+ );
+
+ final Asset asset = assetRepo
+ .findByPath(contentSection, assetPath)
+ .orElseThrow(
+ () -> new WebApplicationException(
+ Response
+ .status(Response.Status.NOT_FOUND)
+ .entity(
+ String.format(
+ "No asset for path %s found in section %s.",
+ assetPath,
+ contentSection.getLabel()
+ )
+ )
+ .build()
+ )
+ );
+
+ if (!(asset instanceof FileAsset)) {
+ throw new WebApplicationException(
+ Response
+ .status(Response.Status.NOT_FOUND)
+ .entity(
+ String.format(
+ "No asset for path %s found in section %s.",
+ assetPath,
+ contentSection.getLabel()
+ )
+ )
+ .build()
+ );
+ }
+ final FileAsset fileAsset = (FileAsset) asset;
+ try ( InputStream dataInputStream = dataService.retrieveData(fileAsset)) {
+
+ final StreamingOutput output = new StreamingOutput() {
+
+ @Override
+ public void write(final OutputStream outputStream)
+ throws IOException, WebApplicationException {
+ byte[] buffer = new byte[8192];
+ int length;
+ while ((length = dataInputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, length);
+ }
+ }
+
+ };
+
+ return Response
+ .ok()
+ .entity(output)
+ .header("Content-Type", fileAsset.getMimeType())
+ .header(
+ "Content-Disposition",
+ String.format(
+ "attachment; filename=\"%s\"",
+ fileAsset.getFileName()
+ )
+ )
+ .build();
+
+ } catch (IOException ex) {
+ throw new WebApplicationException(
+ ex,
+ Response.Status.INTERNAL_SERVER_ERROR
+ );
+ }
+ }
+
+// private class FileAssetOutput implements StreamingOutput {
+//
+// @Override
+// public void write(final OutputStream outputStream)
+// throws IOException, WebApplicationException {
+//
+// }
+//
+// }
+}
diff --git a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contentsection/assets/fileasset/edit-fileasset.xhtml b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contentsection/assets/fileasset/edit-fileasset.xhtml
index 1d1bab73f..ceca43498 100644
--- a/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contentsection/assets/fileasset/edit-fileasset.xhtml
+++ b/ccm-cms/src/main/resources/WEB-INF/views/org/librecms/ui/contentsection/assets/fileasset/edit-fileasset.xhtml
@@ -52,14 +52,19 @@