diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/AudioAssetCreateStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/AudioAssetCreateStep.java
new file mode 100644
index 000000000..94d334dfa
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/AudioAssetCreateStep.java
@@ -0,0 +1,101 @@
+/*
+ * 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.l10n.GlobalizationHelper;
+import org.librecms.assets.AudioAsset;
+import org.librecms.contentsection.AssetRepository;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Named("CmsAudioAssetCreateStep")
+public class AudioAssetCreateStep extends AbstractMvcAssetCreateStep {
+
+ @Inject
+ private AssetRepository assetRepo;
+
+ @Inject
+ private GlobalizationHelper globalizationHelper;
+
+ private String description;
+
+ @Override
+ public String showCreateStep() {
+ return "org/librecms/ui/contentsection/assets/audioasset/create-audioasset.xhtml";
+ }
+
+ @Override
+ public String getLabel() {
+ return globalizationHelper
+ .getLocalizedTextsUtil(getBundle())
+ .getText("audioasset.label");
+ }
+
+ @Override
+ public String getDescription() {
+ return globalizationHelper
+ .getLocalizedTextsUtil(getBundle())
+ .getText("audioasset.description");
+ }
+
+ @Override
+ public String getBundle() {
+ return MvcAssetStepsConstants.BUNDLE;
+ }
+
+ @Override
+ protected Class getAssetClass() {
+ return AudioAsset.class;
+ }
+
+ @Override
+ protected String setAssetProperties(
+ final AudioAsset asset, final Map formParams
+ ) {
+ description = Optional
+ .ofNullable(formParams.get("description"))
+ .filter(value -> value.length > 0)
+ .map(value -> value[0])
+ .orElse("");
+ asset.getDescription().addValue(
+ new Locale(getInitialLocale()), description
+ );
+
+ assetRepo.save(asset);
+
+ return String.format(
+ "redirect:/%s/assets/%s/%s/@audioasset-edit",
+ getContentSectionLabel(),
+ getFolderPath(),
+ getName()
+ );
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/AudioAssetEditStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/AudioAssetEditStep.java
new file mode 100644
index 000000000..fa9f59ac2
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/AudioAssetEditStep.java
@@ -0,0 +1,488 @@
+/*
+ * 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.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.jboss.resteasy.plugins.providers.multipart.InputPart;
+import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
+import org.libreccm.api.Identifier;
+import org.libreccm.api.IdentifierParser;
+import org.libreccm.core.UnexpectedErrorException;
+import org.libreccm.l10n.GlobalizationHelper;
+import org.libreccm.security.AuthorizationRequired;
+import org.librecms.assets.BinaryAssetDataService;
+import org.librecms.assets.FileAsset;
+import org.librecms.assets.AudioAsset;
+import org.librecms.assets.LegalMetadata;
+import org.librecms.contentsection.AssetRepository;
+import org.librecms.ui.contentsections.AssetPermissionsChecker;
+import org.librecms.ui.contentsections.ContentSectionNotFoundException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.activation.MimeType;
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.mvc.Controller;
+import javax.mvc.Models;
+import javax.transaction.Transactional;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Path(MvcAssetEditSteps.PATH_PREFIX + "audioasset-edit")
+@Controller
+@MvcAssetEditStepDef(
+ bundle = MvcAssetStepsConstants.BUNDLE,
+ descriptionKey = "audioasset.editstep.description",
+ labelKey = "audioasset.editstep.label",
+ supportedAssetType = FileAsset.class
+)
+public class AudioAssetEditStep extends AbstractMvcAssetEditStep {
+
+ private static final Logger LOGGER = LogManager.getLogger(
+ AudioAssetEditStep.class
+ );
+
+ @Inject
+ private AssetStepsDefaultMessagesBundle messageBundle;
+
+ @Inject
+ private AssetUi assetUi;
+
+ @Inject
+ private AssetRepository assetRepo;
+
+ @Inject
+ private BinaryAssetDataService dataService;
+
+ @Inject
+ private GlobalizationHelper globalizationHelper;
+
+ @Inject
+ private IdentifierParser identifierParser;
+
+ @Inject
+ private AssetPermissionsChecker assetPermissionsChecker;
+
+ @Inject
+ private Models models;
+
+ @Inject
+ private AudioAssetEditStepModel editStepModel;
+
+ @Override
+ public Class extends MvcAssetEditStep> getStepClass() {
+ return AudioAssetEditStep.class;
+ }
+
+ @Override
+ protected void init() throws ContentSectionNotFoundException,
+ AssetNotFoundException {
+ super.init();
+
+ if (getAsset() instanceof AudioAsset) {
+ editStepModel.setDescriptionValues(
+ getAudioAsset()
+ .getDescription()
+ .getValues()
+ .entrySet()
+ .stream()
+ .collect(
+ Collectors.toMap(
+ entry -> entry.getKey().toString(),
+ entry -> entry.getValue()
+ )
+ )
+ );
+
+ final Set descriptionLocales = getAudioAsset()
+ .getDescription()
+ .getAvailableLocales();
+ editStepModel.setUnusedDescriptionLocales(
+ globalizationHelper
+ .getAvailableLocales()
+ .stream()
+ .filter(locale -> !descriptionLocales.contains(locale))
+ .map(Locale::toString)
+ .collect(Collectors.toList())
+ );
+
+ editStepModel.setFileName(getAudioAsset().getFileName());
+ editStepModel.setMimeType(
+ Optional
+ .ofNullable(getAudioAsset().getMimeType())
+ .map(MimeType::toString)
+ .orElse("")
+ );
+ editStepModel.setSize(getAudioAsset().getSize());
+
+ final long size = getAudioAsset().getSize();
+ if (size < 2048) {
+ editStepModel.setSizeLabel(String.format("%d Bytes", size));
+ } else if (size < 1024 * 1024) {
+ editStepModel.setSizeLabel(
+ String.format("%d kB", size / 1024)
+ );
+ } else if (size < 1024 * 1024 * 1024) {
+ editStepModel.setSizeLabel(
+ String.format("%d MB", size / (1024 * 1024))
+ );
+ } else {
+ editStepModel.setSizeLabel(
+ String.format("%d GB", size / (1024 * 1024 * 1024))
+ );
+ }
+
+ editStepModel.setLegalMetadata(getAudioAsset().getLegalMetadata());
+ } else {
+ throw new AssetNotFoundException(
+ assetUi.showAssetNotFound(
+ getContentSection(), getAssetPath()
+ ),
+ String.format(
+ "No asset for path %s found in section %s.",
+ getAssetPath(),
+ getContentSection().getLabel()
+ )
+ );
+ }
+ }
+
+ @GET
+ @Path("/")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ @Override
+ public String showStep(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ return "org/librecms/ui/contentsection/assets/audioasset/edit-audioasset.xhtml";
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+ @POST
+ @Path("/description/add")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String addDescription(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath,
+ @FormParam("locale") final String localeParam,
+ @FormParam("value") final String value
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ final Locale locale = new Locale(localeParam);
+ final AudioAsset asset = getAudioAsset();
+ asset.getDescription().addValue(locale, value);
+
+ assetRepo.save(asset);
+
+ return buildRedirectPathForStep();
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+ @POST
+ @Path("/description/edit/{locale}")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String editDescription(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath,
+ @PathParam("locale") final String localeParam,
+ @FormParam("value") final String value
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ final Locale locale = new Locale(localeParam);
+ final AudioAsset asset = getAudioAsset();
+ asset.getDescription().addValue(locale, value);
+
+ assetRepo.save(asset);
+
+ return buildRedirectPathForStep();
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+ @POST
+ @Path("/description/remove/{locale}")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String removeDescription(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath,
+ @PathParam("locale") final String localeParam
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ final Locale locale = new Locale(localeParam);
+ final AudioAsset asset = getAudioAsset();
+ asset.getDescription().removeValue(locale);
+
+ assetRepo.save(asset);
+
+ return buildRedirectPathForStep();
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+ @POST
+ @Path("/legalmetadata")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String setLegalMetadata(
+ @FormParam("legalMetadataIdentifier")
+ final String legalMetadataIdentifier
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ final Identifier identifier = identifierParser
+ .parseIdentifier(legalMetadataIdentifier);
+ final Optional legalMetadataResult;
+ switch (identifier.getType()) {
+ case ID:
+ legalMetadataResult = assetRepo.findById(
+ Long.parseLong(identifier.getIdentifier()),
+ LegalMetadata.class
+ );
+ break;
+ case UUID:
+ legalMetadataResult = assetRepo.findByUuidAndType(
+ identifier.getIdentifier(),
+ LegalMetadata.class
+ );
+ break;
+ default:
+ legalMetadataResult = assetRepo
+ .findByPath(identifier.getIdentifier())
+ .map(result -> (LegalMetadata) result);
+ break;
+ }
+ if (!legalMetadataResult.isPresent()) {
+ return showLegalMetadataNotFound(legalMetadataIdentifier);
+ }
+
+ final LegalMetadata legalMetadata = legalMetadataResult.get();
+
+ getAudioAsset().setLegalMetadata(legalMetadata);
+ assetRepo.save(getAudioAsset());
+
+ return buildRedirectPathForStep();
+ }
+
+ @POST
+ @Path("/legalmetadata/@remove")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String removeLegalMetadata() {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ getAudioAsset().setLegalMetadata(null);
+ assetRepo.save(getAudioAsset());
+
+ return buildRedirectPathForStep();
+ }
+
+ private String showLegalMetadataNotFound(
+ final String legalMetadataIdentifer
+ ) {
+ models.put("legalMetadataIdentifier", legalMetadataIdentifer);
+ return "org/librecms/ui/contentsection/assets/external-video-asset/legal-metadata-not-found.xhtml";
+ }
+
+ @POST
+ @Path("/upload")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String uploadFile(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath,
+ final MultipartFormDataInput input
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ final AudioAsset asset = getAudioAsset();
+
+ final Map> uploadForm = input
+ .getFormDataMap();
+ final List inputParts = uploadForm.get("fileData");
+
+ String fileName = "";
+ String contentType = "";
+ for (final InputPart inputPart : inputParts) {
+ try {
+ final MultivaluedMap headers = inputPart
+ .getHeaders();
+
+ fileName = getFileName(headers);
+ contentType = getContentType(headers);
+
+ dataService.saveData(
+ asset,
+ inputPart.getBody(InputStream.class, null),
+ fileName,
+ contentType
+ );
+ } catch (IOException | UnexpectedErrorException ex) {
+ LOGGER.error(
+ "Failed to upload file for FileAsset {}:", assetPath
+ );
+ LOGGER.error(ex);
+
+ models.put("uploadFailed", true);
+ return buildRedirectPathForStep();
+ }
+ }
+
+ return buildRedirectPathForStep();
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+
+ public AudioAsset getAudioAsset() {
+ return (AudioAsset) getAsset();
+ }
+
+ private String getFileName(final MultivaluedMap headers) {
+ final String[] contentDisposition = headers
+ .getFirst("Content-Disposition")
+ .split(";");
+
+ for (final String fileName : contentDisposition) {
+ if (fileName.trim().startsWith("filename")) {
+ final String[] name = fileName.split("=");
+
+ return name[1].trim().replaceAll("\"", "");
+ }
+ }
+
+ return "";
+ }
+
+ private String getContentType(
+ final MultivaluedMap headers
+ ) {
+ return headers.getFirst("Content-Type");
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/AudioAssetEditStepDownload.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/AudioAssetEditStepDownload.java
new file mode 100644
index 000000000..82a94fc08
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/AudioAssetEditStepDownload.java
@@ -0,0 +1,139 @@
+/*
+ * 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.AudioAsset;
+import org.librecms.assets.BinaryAssetDataService;
+import org.librecms.contentsection.Asset;
+import org.librecms.contentsection.AssetRepository;
+import org.librecms.contentsection.ContentSection;
+import org.librecms.ui.contentsections.ContentSectionsUi;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+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 + "audioasset-edit-download")
+public class AudioAssetEditStepDownload {
+
+ @Inject
+ private AssetRepository assetRepo;
+
+ @Inject
+ private BinaryAssetDataService dataService;
+
+ @Inject
+ private ContentSectionsUi sectionsUi;
+
+ @GET
+ @Path("/")
+ @AuthorizationRequired
+ 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 AudioAsset)) {
+ 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 AudioAsset audioAsset = (AudioAsset) asset;
+ return Response
+ .ok()
+ .entity(
+ new StreamingOutput() {
+
+ @Override
+ public void write(final OutputStream outputStream)
+ throws IOException, WebApplicationException {
+ dataService.copyDataToOutputStream(
+ audioAsset, outputStream
+ );
+ }
+
+ })
+ .header("Content-Type", audioAsset.getMimeType())
+ .header(
+ "Content-Disposition",
+ String.format(
+ "attachment; filename=\"%s\"",
+ audioAsset.getFileName()
+ )
+ )
+ .build();
+ }
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/AudioAssetEditStepModel.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/AudioAssetEditStepModel.java
new file mode 100644
index 000000000..4c717133d
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/AudioAssetEditStepModel.java
@@ -0,0 +1,117 @@
+/*
+ * 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.librecms.assets.LegalMetadata;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Named;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Named("CmsAudioAssetEditStepModel")
+public class AudioAssetEditStepModel {
+
+ private Map descriptionValues;
+
+ private List unusedDescriptionLocales;
+
+ private String fileName;
+
+ private String mimeType;
+
+ private long size;
+
+ private String sizeLabel;
+
+ private LegalMetadata legalMetadata;
+
+ public Map getDescriptionValues() {
+ return Collections.unmodifiableMap(descriptionValues);
+ }
+
+ protected void setDescriptionValues(
+ final Map descriptionValues
+ ) {
+ this.descriptionValues = new HashMap<>(descriptionValues);
+ }
+
+ public List getUnusedDescriptionLocales() {
+ return Collections.unmodifiableList(unusedDescriptionLocales);
+ }
+
+ protected void setUnusedDescriptionLocales(
+ final List descriptionLocales
+ ) {
+ this.unusedDescriptionLocales = new ArrayList<>(descriptionLocales);
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ protected void setFileName(final String fileName) {
+ this.fileName = fileName;
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ protected void setMimeType(final String mimeType) {
+ this.mimeType = mimeType;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ protected void setSize(final long size) {
+ this.size = size;
+ }
+
+ public String getSizeLabel() {
+ return sizeLabel;
+ }
+
+ protected void setSizeLabel(final String sizeLabel) {
+ this.sizeLabel = sizeLabel;
+ }
+
+ public LegalMetadata getLegalMetadata() {
+ return legalMetadata;
+ }
+
+ protected void setLegalMetadata(final LegalMetadata legalMetadata) {
+ this.legalMetadata = legalMetadata;
+ }
+
+ public String getLegalMetadataType() {
+ return LegalMetadata.class.getName();
+ }
+}
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 fa80ec05b..bd0f6b46f 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
@@ -20,7 +20,6 @@ package org.librecms.ui.contentsections.assets;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import org.hibernate.engine.jdbc.BlobProxy;
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
import org.libreccm.core.UnexpectedErrorException;
@@ -34,9 +33,6 @@ import org.librecms.ui.contentsections.ContentSectionNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.sql.Blob;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -45,7 +41,6 @@ import java.util.Set;
import java.util.stream.Collectors;
import javax.activation.MimeType;
-import javax.activation.MimeTypeParseException;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.mvc.Controller;
@@ -208,10 +203,6 @@ public class FileAssetEditStep extends AbstractMvcAssetEditStep {
}
}
- public FileAsset getFileAsset() {
- return (FileAsset) getAsset();
- }
-
@POST
@Path("/description/add")
@AuthorizationRequired
@@ -234,10 +225,10 @@ public class FileAssetEditStep extends AbstractMvcAssetEditStep {
if (assetPermissionsChecker.canEditAsset(getAsset())) {
final Locale locale = new Locale(localeParam);
- final FileAsset bookmark = getFileAsset();
- bookmark.getDescription().addValue(locale, value);
+ final FileAsset fileAsset = getFileAsset();
+ fileAsset.getDescription().addValue(locale, value);
- assetRepo.save(bookmark);
+ assetRepo.save(fileAsset);
return buildRedirectPathForStep();
} else {
@@ -270,10 +261,10 @@ public class FileAssetEditStep extends AbstractMvcAssetEditStep {
if (assetPermissionsChecker.canEditAsset(getAsset())) {
final Locale locale = new Locale(localeParam);
- final FileAsset bookmark = getFileAsset();
- bookmark.getDescription().addValue(locale, value);
+ final FileAsset fileAsset = getFileAsset();
+ fileAsset.getDescription().addValue(locale, value);
- assetRepo.save(bookmark);
+ assetRepo.save(fileAsset);
return buildRedirectPathForStep();
} else {
@@ -305,10 +296,10 @@ public class FileAssetEditStep extends AbstractMvcAssetEditStep {
if (assetPermissionsChecker.canEditAsset(getAsset())) {
final Locale locale = new Locale(localeParam);
- final FileAsset bookmark = getFileAsset();
- bookmark.getDescription().removeValue(locale);
+ final FileAsset fileAsset = getFileAsset();
+ fileAsset.getDescription().removeValue(locale);
- assetRepo.save(bookmark);
+ assetRepo.save(fileAsset);
return buildRedirectPathForStep();
} else {
@@ -380,7 +371,10 @@ public class FileAssetEditStep extends AbstractMvcAssetEditStep {
getAsset(),
messageBundle.get("asset.edit.denied"));
}
+ }
+ public FileAsset getFileAsset() {
+ return (FileAsset) getAsset();
}
private String getFileName(final MultivaluedMap headers) {
@@ -405,18 +399,4 @@ public class FileAssetEditStep extends AbstractMvcAssetEditStep {
return headers.getFirst("Content-Type");
}
- private long getFileSize(final MultivaluedMap headers) {
- if (headers.containsKey("Content-Length")) {
- try {
- return Long.parseLong(
- headers.getFirst("Content-Length")
- );
- } catch (NumberFormatException ex) {
- throw new UnexpectedErrorException(ex);
- }
- } else {
- return 0;
- }
- }
-
}
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
index ee4e99d51..de35092fc 100644
--- 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
@@ -31,7 +31,6 @@ import java.io.OutputStream;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
-import javax.transaction.Transactional;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@@ -45,7 +44,6 @@ import javax.ws.rs.core.StreamingOutput;
*/
@RequestScoped
@Path(MvcAssetEditSteps.PATH_PREFIX + "fileasset-edit-download")
-
public class FileAssetEditStepDownload {
@Inject
diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/ImageCreateStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/ImageCreateStep.java
new file mode 100644
index 000000000..4ebc3a880
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/ImageCreateStep.java
@@ -0,0 +1,101 @@
+/*
+ * 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.l10n.GlobalizationHelper;
+import org.librecms.assets.Image;
+import org.librecms.contentsection.AssetRepository;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Named("CmsImageCreatStep")
+public class ImageCreateStep extends AbstractMvcAssetCreateStep {
+
+ @Inject
+ private AssetRepository assetRepository;
+
+ @Inject
+ private GlobalizationHelper globalizationHelper;
+
+ private String description;
+
+ @Override
+ public String showCreateStep() {
+ return "org/librecms/ui/contentsection/assets/image/create-image.xhtml";
+ }
+
+ @Override
+ public String getLabel() {
+ return globalizationHelper
+ .getLocalizedTextsUtil(getBundle())
+ .getText("image.label");
+ }
+
+ @Override
+ public String getDescription() {
+ return globalizationHelper
+ .getLocalizedTextsUtil(getBundle())
+ .getText("image.description");
+ }
+
+ @Override
+ public String getBundle() {
+ return MvcAssetStepsConstants.BUNDLE;
+ }
+
+ @Override
+ protected Class getAssetClass() {
+ return Image.class;
+ }
+
+ @Override
+ protected String setAssetProperties(
+ final Image image, final Map formParams
+ ) {
+ description = Optional
+ .ofNullable(formParams.get("description"))
+ .filter(value -> value.length > 0)
+ .map(value -> value[0])
+ .orElse("");
+ image.getDescription().addValue(
+ new Locale(getInitialLocale()), description
+ );
+
+ assetRepository.save(image);
+
+ return String.format(
+ "redirect:/%s/assets/%s/%s/@image-edit",
+ getContentSectionLabel(),
+ getFolderPath(),
+ getName()
+ );
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/ImageEditStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/ImageEditStep.java
new file mode 100644
index 000000000..390ca9e7b
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/ImageEditStep.java
@@ -0,0 +1,489 @@
+/*
+ * 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.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.jboss.resteasy.plugins.providers.multipart.InputPart;
+import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
+import org.libreccm.api.Identifier;
+import org.libreccm.api.IdentifierParser;
+import org.libreccm.core.UnexpectedErrorException;
+import org.libreccm.l10n.GlobalizationHelper;
+import org.libreccm.security.AuthorizationRequired;
+import org.librecms.assets.BinaryAssetDataService;
+import org.librecms.assets.FileAsset;
+import org.librecms.assets.Image;
+import org.librecms.assets.LegalMetadata;
+import org.librecms.contentsection.AssetRepository;
+import org.librecms.ui.contentsections.AssetPermissionsChecker;
+import org.librecms.ui.contentsections.ContentSectionNotFoundException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.activation.MimeType;
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.mvc.Controller;
+import javax.mvc.Models;
+import javax.transaction.Transactional;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Path(MvcAssetEditSteps.PATH_PREFIX + "image-edit")
+@Controller
+@MvcAssetEditStepDef(
+ bundle = MvcAssetStepsConstants.BUNDLE,
+ descriptionKey = "image.editstep.description",
+ labelKey = "image.editstep.label",
+ supportedAssetType = FileAsset.class
+)
+public class ImageEditStep extends AbstractMvcAssetEditStep {
+
+ private static final Logger LOGGER = LogManager.getLogger(
+ ImageEditStep.class
+ );
+
+ @Inject
+ private AssetStepsDefaultMessagesBundle messageBundle;
+
+ @Inject
+ private AssetUi assetUi;
+
+ @Inject
+ private AssetRepository assetRepo;
+
+ @Inject
+ private BinaryAssetDataService dataService;
+
+ @Inject
+ private GlobalizationHelper globalizationHelper;
+
+ @Inject
+ private IdentifierParser identifierParser;
+
+ @Inject
+ private AssetPermissionsChecker assetPermissionsChecker;
+
+ @Inject
+ private Models models;
+
+ @Inject
+ private ImageEditStepModel editStepModel;
+
+ @Override
+ public Class extends MvcAssetEditStep> getStepClass() {
+ return ImageEditStep.class;
+ }
+
+ @Override
+ public void init() throws ContentSectionNotFoundException,
+ AssetNotFoundException {
+ super.init();
+
+ if (getAsset() instanceof Image) {
+ editStepModel.setDescriptionValues(
+ getImage()
+ .getDescription()
+ .getValues()
+ .entrySet()
+ .stream()
+ .collect(
+ Collectors.toMap(
+ entry -> entry.getKey().toString(),
+ entry -> entry.getValue()
+ )
+ )
+ );
+
+ final Set descriptionLocales = getImage()
+ .getDescription()
+ .getAvailableLocales();
+ editStepModel.setUnusedDescriptionLocales(
+ globalizationHelper
+ .getAvailableLocales()
+ .stream()
+ .filter(locale -> !descriptionLocales.contains(locale))
+ .map(Locale::toString)
+ .collect(Collectors.toList())
+ );
+
+ editStepModel.setFileName(getImage().getFileName());
+ editStepModel.setMimeType(
+ Optional
+ .ofNullable(getImage().getMimeType())
+ .map(MimeType::toString)
+ .orElse("")
+ );
+ editStepModel.setSize(getImage().getSize());
+ final long size = getImage().getSize();
+ if (size < 2048) {
+ editStepModel.setSizeLabel(String.format("%d Bytes", size));
+ } else if (size < 1024 * 1024) {
+ editStepModel.setSizeLabel(
+ String.format("%d kB", size / 1024)
+ );
+ } else if (size < 1024 * 1024 * 1024) {
+ editStepModel.setSizeLabel(
+ String.format("%d MB", size / (1024 * 1024))
+ );
+ } else {
+ editStepModel.setSizeLabel(
+ String.format("%d GB", size / (1024 * 1024 * 1024))
+ );
+ }
+
+ editStepModel.setHeight(getImage().getHeight());
+ editStepModel.setWidth(getImage().getWidth());
+
+ editStepModel.setLegalMetadata(getImage().getLegalMetadata());
+ } else {
+ throw new AssetNotFoundException(
+ assetUi.showAssetNotFound(
+ getContentSection(), getAssetPath()
+ ),
+ String.format(
+ "No image for path %s found in section %s.",
+ getAssetPath(),
+ getContentSection().getLabel()
+ )
+ );
+ }
+ }
+
+ @GET
+ @Path("/")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ @Override
+ public String showStep(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ return "org/librecms/ui/contentsection/assets/image/edit-image.xhtml";
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+ @POST
+ @Path("/description/add")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String addDescription(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath,
+ @FormParam("locale") final String localeParam,
+ @FormParam("value") final String value
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ final Locale locale = new Locale(localeParam);
+ final Image image = getImage();
+ image.getDescription().addValue(locale, value);
+
+ assetRepo.save(image);
+
+ return buildRedirectPathForStep();
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+ @POST
+ @Path("/description/edit/{locale}")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String editDescription(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath,
+ @PathParam("locale") final String localeParam,
+ @FormParam("value") final String value
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ final Locale locale = new Locale(localeParam);
+ final Image image = getImage();
+ image.getDescription().addValue(locale, value);
+
+ assetRepo.save(image);
+
+ return buildRedirectPathForStep();
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+ @POST
+ @Path("/description/remove/{locale}")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String removeDescription(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath,
+ @PathParam("locale") final String localeParam
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ final Locale locale = new Locale(localeParam);
+ final Image image = getImage();
+ image.getDescription().removeValue(locale);
+
+ assetRepo.save(image);
+
+ return buildRedirectPathForStep();
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+ @POST
+ @Path("/legalmetadata")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String setLegalMetadata(
+ @FormParam("legalMetadataIdentifier")
+ final String legalMetadataIdentifier
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ final Identifier identifier = identifierParser
+ .parseIdentifier(legalMetadataIdentifier);
+ final Optional legalMetadataResult;
+ switch (identifier.getType()) {
+ case ID:
+ legalMetadataResult = assetRepo.findById(
+ Long.parseLong(identifier.getIdentifier()),
+ LegalMetadata.class
+ );
+ break;
+ case UUID:
+ legalMetadataResult = assetRepo.findByUuidAndType(
+ identifier.getIdentifier(),
+ LegalMetadata.class
+ );
+ break;
+ default:
+ legalMetadataResult = assetRepo
+ .findByPath(identifier.getIdentifier())
+ .map(result -> (LegalMetadata) result);
+ break;
+ }
+ if (!legalMetadataResult.isPresent()) {
+ return showLegalMetadataNotFound(legalMetadataIdentifier);
+ }
+
+ final LegalMetadata legalMetadata = legalMetadataResult.get();
+
+ getImage().setLegalMetadata(legalMetadata);
+ assetRepo.save(getImage());
+
+ return buildRedirectPathForStep();
+ }
+
+ @POST
+ @Path("/legalmetadata/@remove")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String removeLegalMetadata() {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ getImage().setLegalMetadata(null);
+ assetRepo.save(getImage());
+
+ return buildRedirectPathForStep();
+ }
+
+ private String showLegalMetadataNotFound(
+ final String legalMetadataIdentifer
+ ) {
+ models.put("legalMetadataIdentifier", legalMetadataIdentifer);
+ return "org/librecms/ui/contentsection/assets/external-video-asset/legal-metadata-not-found.xhtml";
+ }
+
+ @POST
+ @Path("/upload")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String uploadFile(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath,
+ final MultipartFormDataInput input
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ final Image image = getImage();
+
+ final Map> uploadForm = input
+ .getFormDataMap();
+ final List inputParts = uploadForm.get("fileData");
+
+ String fileName = "";
+ String contentType = "";
+ for (final InputPart inputPart : inputParts) {
+ try {
+ final MultivaluedMap headers = inputPart
+ .getHeaders();
+
+ fileName = getFileName(headers);
+ contentType = getContentType(headers);
+
+ dataService.saveData(
+ image,
+ inputPart.getBody(InputStream.class, null),
+ fileName,
+ contentType
+ );
+ } catch (IOException | UnexpectedErrorException ex) {
+ LOGGER.error(
+ "Failed to upload file for FileAsset {}:", assetPath
+ );
+ LOGGER.error(ex);
+
+ models.put("uploadFailed", true);
+ return buildRedirectPathForStep();
+ }
+ }
+
+ return buildRedirectPathForStep();
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+ public Image getImage() {
+ return (Image) getAsset();
+ }
+
+ private String getFileName(final MultivaluedMap headers) {
+ final String[] contentDisposition = headers
+ .getFirst("Content-Disposition")
+ .split(";");
+
+ for (final String fileName : contentDisposition) {
+ if (fileName.trim().startsWith("filename")) {
+ final String[] name = fileName.split("=");
+
+ return name[1].trim().replaceAll("\"", "");
+ }
+ }
+
+ return "";
+ }
+
+ private String getContentType(
+ final MultivaluedMap headers
+ ) {
+ return headers.getFirst("Content-Type");
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/ImageEditStepDownload.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/ImageEditStepDownload.java
new file mode 100644
index 000000000..81ab90752
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/ImageEditStepDownload.java
@@ -0,0 +1,164 @@
+/*
+ * 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.Image;
+import org.librecms.assets.ImageService;
+import org.librecms.contentsection.Asset;
+import org.librecms.contentsection.AssetRepository;
+import org.librecms.contentsection.ContentSection;
+import org.librecms.ui.contentsections.ContentSectionsUi;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+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 + "image-edit-download")
+public class ImageEditStepDownload {
+
+ @Inject
+ private AssetRepository assetRepo;
+
+ @Inject
+ private BinaryAssetDataService dataService;
+
+ @Inject
+ private ContentSectionsUi sectionsUi;
+
+ @Inject
+ private ImageService imageService;
+
+ @GET
+ @Path("/")
+ @AuthorizationRequired
+ public Response downloadFile(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath,
+ @QueryParam("width") @DefaultValue("-1")
+ final int width,
+ @QueryParam("height") @DefaultValue("-1")
+ final int height
+ ) {
+ 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 Image)) {
+ throw new WebApplicationException(
+ Response
+ .status(Response.Status.NOT_FOUND)
+ .entity(
+ String.format(
+ "No image for path %s found in section %s.",
+ assetPath,
+ contentSection.getLabel()
+ )
+ )
+ .build()
+ );
+ }
+
+ final Image image = (Image) asset;
+ final StreamingOutput streamingOutput;
+ if (width == -1 && height == -1) {
+ streamingOutput = new StreamingOutput() {
+
+ @Override
+ public void write(final OutputStream outputStream)
+ throws IOException, WebApplicationException {
+ dataService.copyDataToOutputStream(image, outputStream);
+ }
+
+ };
+ } else {
+ final byte[] scaled = imageService.scaleImage(image, width, height);
+ streamingOutput = new StreamingOutput() {
+
+ @Override
+ public void write(final OutputStream outputStream)
+ throws IOException, WebApplicationException {
+ outputStream.write(scaled);
+ }
+
+ };
+ }
+
+ return Response
+ .ok()
+ .entity(streamingOutput)
+ .header("Content-Type", image.getMimeType())
+ .header(
+ "Content-Disposition",
+ String.format(
+ "attachment; filename=\"%s\"",
+ image.getFileName()
+ )
+ )
+ .build();
+
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/ImageEditStepModel.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/ImageEditStepModel.java
new file mode 100644
index 000000000..b2e806f8f
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/ImageEditStepModel.java
@@ -0,0 +1,138 @@
+/*
+ * 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.librecms.assets.LegalMetadata;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Named;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Named("CmsImageEditStepModel")
+public class ImageEditStepModel {
+
+ private Map descriptionValues;
+
+ private List unusedDescriptionLocales;
+
+ private String fileName;
+
+ private String mimeType;
+
+ private long size;
+
+ private String sizeLabel;
+
+ private long width;
+
+ private long height;
+
+ private LegalMetadata legalMetadata;
+
+ public Map getDescriptionValues() {
+ return Collections.unmodifiableMap(descriptionValues);
+ }
+
+ protected void setDescriptionValues(
+ final Map descriptionValues
+ ) {
+ this.descriptionValues = new HashMap<>(descriptionValues);
+ }
+
+ public List getUnusedDescriptionLocales() {
+ return Collections.unmodifiableList(unusedDescriptionLocales);
+ }
+
+ protected void setUnusedDescriptionLocales(
+ final List descriptionLocales
+ ) {
+ this.unusedDescriptionLocales = new ArrayList<>(descriptionLocales);
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ protected void setFileName(final String fileName) {
+ this.fileName = fileName;
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ protected void setMimeType(final String mimeType) {
+ this.mimeType = mimeType;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ protected void setSize(final long size) {
+ this.size = size;
+ }
+
+ public String getSizeLabel() {
+ return sizeLabel;
+ }
+
+ protected void setSizeLabel(final String sizeLabel) {
+ this.sizeLabel = sizeLabel;
+ }
+
+ public long getWidth() {
+ return width;
+ }
+
+ protected void setWidth(final long width) {
+ this.width = width;
+ }
+
+ public long getHeight() {
+ return height;
+ }
+
+ protected void setHeight(final long height) {
+ this.height = height;
+ }
+
+ public LegalMetadata getLegalMetadata() {
+ return legalMetadata;
+ }
+
+ protected void setLegalMetadata(final LegalMetadata legalMetadata) {
+ this.legalMetadata = legalMetadata;
+ }
+
+ public String getLegalMetadataType() {
+ return LegalMetadata.class.getName();
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/VideoAssetCreateStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/VideoAssetCreateStep.java
new file mode 100644
index 000000000..ff191bbd6
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/VideoAssetCreateStep.java
@@ -0,0 +1,102 @@
+/*
+ * 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.l10n.GlobalizationHelper;
+import org.librecms.assets.FileAsset;
+import org.librecms.assets.VideoAsset;
+import org.librecms.contentsection.AssetRepository;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Named("CmsVideoAssetCreateStep")
+public class VideoAssetCreateStep extends AbstractMvcAssetCreateStep{
+
+ @Inject
+ private AssetRepository assetRepo;
+
+ @Inject
+ private GlobalizationHelper globalizationHelper;
+
+ private String fileDescription;
+
+ @Override
+ public String showCreateStep() {
+ return "org/librecms/ui/contentsection/assets/videoasset/create-videoasset.xhtml";
+ }
+
+ @Override
+ public String getLabel() {
+ return globalizationHelper
+ .getLocalizedTextsUtil(getBundle())
+ .getText("videoasset.label");
+ }
+
+ @Override
+ public String getDescription() {
+ return globalizationHelper
+ .getLocalizedTextsUtil(getBundle())
+ .getText("videoasset.description");
+ }
+
+ @Override
+ public String getBundle() {
+ return MvcAssetStepsConstants.BUNDLE;
+ }
+
+ @Override
+ protected Class getAssetClass() {
+ return VideoAsset.class;
+ }
+
+ @Override
+ protected String setAssetProperties(
+ final VideoAsset asset, final Map formParams
+ ) {
+ fileDescription = Optional
+ .ofNullable(formParams.get("description"))
+ .filter(value -> value.length > 0)
+ .map(value -> value[0])
+ .orElse("");
+ asset.getDescription().addValue(
+ new Locale(getInitialLocale()), fileDescription
+ );
+
+ assetRepo.save(asset);
+
+ return String.format(
+ "redirect:/%s/assets/%s/%s/@videoasset-edit",
+ getContentSectionLabel(),
+ getFolderPath(),
+ getName()
+ );
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/VideoAssetEditStep.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/VideoAssetEditStep.java
new file mode 100644
index 000000000..c5bb7019f
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/VideoAssetEditStep.java
@@ -0,0 +1,487 @@
+/*
+ * 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.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.jboss.resteasy.plugins.providers.multipart.InputPart;
+import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
+import org.libreccm.api.Identifier;
+import org.libreccm.api.IdentifierParser;
+import org.libreccm.core.UnexpectedErrorException;
+import org.libreccm.l10n.GlobalizationHelper;
+import org.libreccm.security.AuthorizationRequired;
+import org.librecms.assets.BinaryAssetDataService;
+import org.librecms.assets.FileAsset;
+import org.librecms.assets.VideoAsset;
+import org.librecms.assets.LegalMetadata;
+import org.librecms.contentsection.AssetRepository;
+import org.librecms.ui.contentsections.AssetPermissionsChecker;
+import org.librecms.ui.contentsections.ContentSectionNotFoundException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.activation.MimeType;
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.mvc.Controller;
+import javax.mvc.Models;
+import javax.transaction.Transactional;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Path(MvcAssetEditSteps.PATH_PREFIX + "videoasset-edit")
+@Controller
+@MvcAssetEditStepDef(
+ bundle = MvcAssetStepsConstants.BUNDLE,
+ descriptionKey = "videoasset.editstep.description",
+ labelKey = "videoasset.editstep.lable",
+ supportedAssetType = FileAsset.class
+)
+public class VideoAssetEditStep extends AbstractMvcAssetEditStep {
+
+ private static final Logger LOGGER = LogManager.getLogger(VideoAssetEditStep.class
+ );
+
+ @Inject
+ private AssetStepsDefaultMessagesBundle messageBundle;
+
+ @Inject
+ private AssetUi assetUi;
+
+ @Inject
+ private AssetRepository assetRepo;
+
+ @Inject
+ private BinaryAssetDataService dataService;
+
+ @Inject
+ private GlobalizationHelper globalizationHelper;
+
+ @Inject
+ private IdentifierParser identifierParser;
+
+ @Inject
+ private AssetPermissionsChecker assetPermissionsChecker;
+
+ @Inject
+ private Models models;
+
+ @Inject
+ private VideoAssetEditStepModel editStepModel;
+
+ @Override
+ public Class extends MvcAssetEditStep> getStepClass() {
+ return VideoAssetEditStep.class;
+ }
+
+ @Override
+ protected void init() throws ContentSectionNotFoundException,
+ AssetNotFoundException {
+ super.init();
+
+ if (getAsset() instanceof VideoAsset) {
+ editStepModel.setDescriptionValues(
+ getVideoAsset()
+ .getDescription()
+ .getValues()
+ .entrySet()
+ .stream()
+ .collect(
+ Collectors.toMap(
+ entry -> entry.getKey().toString(),
+ entry -> entry.getValue()
+ )
+ )
+ );
+
+ final Set descriptionLocales = getVideoAsset()
+ .getDescription()
+ .getAvailableLocales();
+ editStepModel.setUnusedDescriptionLocales(
+ globalizationHelper
+ .getAvailableLocales()
+ .stream()
+ .filter(locale -> !descriptionLocales.contains(locale))
+ .map(Locale::toString)
+ .collect(Collectors.toList())
+ );
+
+ editStepModel.setFileName(getVideoAsset().getFileName());
+ editStepModel.setMimeType(
+ Optional
+ .ofNullable(getVideoAsset().getMimeType())
+ .map(MimeType::toString)
+ .orElse("")
+ );
+ editStepModel.setSize(getVideoAsset().getSize());
+
+ final long size = getVideoAsset().getSize();
+ if (size < 2048) {
+ editStepModel.setSizeLabel(String.format("%d Bytes", size));
+ } else if (size < 1024 * 1024) {
+ editStepModel.setSizeLabel(
+ String.format("%d kB", size / 1024)
+ );
+ } else if (size < 1024 * 1024 * 1024) {
+ editStepModel.setSizeLabel(
+ String.format("%d MB", size / (1024 * 1024))
+ );
+ } else {
+ editStepModel.setSizeLabel(
+ String.format("%d GB", size / (1024 * 1024 * 1024))
+ );
+ }
+
+ editStepModel.setLegalMetadata(getVideoAsset().getLegalMetadata());
+ } else {
+ throw new AssetNotFoundException(
+ assetUi.showAssetNotFound(
+ getContentSection(), getAssetPath()
+ ),
+ String.format(
+ "No asset for path %s found in section %s.",
+ getAssetPath(),
+ getContentSection().getLabel()
+ )
+ );
+ }
+ }
+
+ @GET
+ @Path("/")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ @Override
+ public String showStep(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ return "org/librecms/ui/contentsection/assets/videoasset/edit-videoasset.xhtml";
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+ @POST
+ @Path("/description/add")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String addDescription(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath,
+ @FormParam("locale") final String localeParam,
+ @FormParam("value") final String value
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ final Locale locale = new Locale(localeParam);
+ final VideoAsset asset = getVideoAsset();
+ asset.getDescription().addValue(locale, value);
+
+ assetRepo.save(asset);
+
+ return buildRedirectPathForStep();
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+ @POST
+ @Path("/description/edit/{locale}")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String editDescription(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath,
+ @PathParam("locale") final String localeParam,
+ @FormParam("value") final String value
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ final Locale locale = new Locale(localeParam);
+ final VideoAsset asset = getVideoAsset();
+ asset.getDescription().addValue(locale, value);
+
+ assetRepo.save(asset);
+
+ return buildRedirectPathForStep();
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+ @POST
+ @Path("/description/remove/{locale}")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String removeDescription(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath,
+ @PathParam("locale") final String localeParam
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ final Locale locale = new Locale(localeParam);
+ final VideoAsset asset = getVideoAsset();
+ asset.getDescription().removeValue(locale);
+
+ assetRepo.save(asset);
+
+ return buildRedirectPathForStep();
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+ @POST
+ @Path("/legalmetadata")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String setLegalMetadata(
+ @FormParam("legalMetadataIdentifier")
+ final String legalMetadataIdentifier
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ final Identifier identifier = identifierParser
+ .parseIdentifier(legalMetadataIdentifier);
+ final Optional legalMetadataResult;
+ switch (identifier.getType()) {
+ case ID:
+ legalMetadataResult = assetRepo.findById(
+ Long.parseLong(identifier.getIdentifier()),
+ LegalMetadata.class
+ );
+ break;
+ case UUID:
+ legalMetadataResult = assetRepo.findByUuidAndType(
+ identifier.getIdentifier(),
+ LegalMetadata.class
+ );
+ break;
+ default:
+ legalMetadataResult = assetRepo
+ .findByPath(identifier.getIdentifier())
+ .map(result -> (LegalMetadata) result);
+ break;
+ }
+ if (!legalMetadataResult.isPresent()) {
+ return showLegalMetadataNotFound(legalMetadataIdentifier);
+ }
+
+ final LegalMetadata legalMetadata = legalMetadataResult.get();
+
+ getVideoAsset().setLegalMetadata(legalMetadata);
+ assetRepo.save(getVideoAsset());
+
+ return buildRedirectPathForStep();
+ }
+
+ @POST
+ @Path("/legalmetadata/@remove")
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String removeLegalMetadata() {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ getVideoAsset().setLegalMetadata(null);
+ assetRepo.save(getVideoAsset());
+
+ return buildRedirectPathForStep();
+ }
+
+ private String showLegalMetadataNotFound(
+ final String legalMetadataIdentifer
+ ) {
+ models.put("legalMetadataIdentifier", legalMetadataIdentifer);
+ return "org/librecms/ui/contentsection/assets/external-video-asset/legal-metadata-not-found.xhtml";
+ }
+
+ @POST
+ @Path("/upload")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @AuthorizationRequired
+ @Transactional(Transactional.TxType.REQUIRED)
+ public String uploadFile(
+ @PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
+ final String sectionIdentifier,
+ @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
+ final String assetPath,
+ final MultipartFormDataInput input
+ ) {
+ try {
+ init();
+ } catch (ContentSectionNotFoundException ex) {
+ return ex.showErrorMessage();
+ } catch (AssetNotFoundException ex) {
+ return ex.showErrorMessage();
+ }
+
+ if (assetPermissionsChecker.canEditAsset(getAsset())) {
+ final VideoAsset asset = getVideoAsset();
+
+ final Map> uploadForm = input
+ .getFormDataMap();
+ final List inputParts = uploadForm.get("fileData");
+
+ String fileName = "";
+ String contentType = "";
+ for (final InputPart inputPart : inputParts) {
+ try {
+ final MultivaluedMap headers = inputPart
+ .getHeaders();
+
+ fileName = getFileName(headers);
+ contentType = getContentType(headers);
+
+ dataService.saveData(
+ asset,
+ inputPart.getBody(InputStream.class, null),
+ fileName,
+ contentType
+ );
+ } catch (IOException | UnexpectedErrorException ex) {
+ LOGGER.error(
+ "Failed to upload file for FileAsset {}:", assetPath
+ );
+ LOGGER.error(ex);
+
+ models.put("uploadFailed", true);
+ return buildRedirectPathForStep();
+ }
+ }
+
+ return buildRedirectPathForStep();
+ } else {
+ return assetUi.showAccessDenied(
+ getContentSection(),
+ getAsset(),
+ messageBundle.get("asset.edit.denied"));
+ }
+ }
+
+
+ public VideoAsset getVideoAsset() {
+ return (VideoAsset) getAsset();
+ }
+
+ private String getFileName(final MultivaluedMap headers) {
+ final String[] contentDisposition = headers
+ .getFirst("Content-Disposition")
+ .split(";");
+
+ for (final String fileName : contentDisposition) {
+ if (fileName.trim().startsWith("filename")) {
+ final String[] name = fileName.split("=");
+
+ return name[1].trim().replaceAll("\"", "");
+ }
+ }
+
+ return "";
+ }
+
+ private String getContentType(
+ final MultivaluedMap headers
+ ) {
+ return headers.getFirst("Content-Type");
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/VideoAssetEditStepDownload.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/VideoAssetEditStepDownload.java
new file mode 100644
index 000000000..d3a10a586
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/VideoAssetEditStepDownload.java
@@ -0,0 +1,139 @@
+/*
+ * 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.VideoAsset;
+import org.librecms.assets.BinaryAssetDataService;
+import org.librecms.contentsection.Asset;
+import org.librecms.contentsection.AssetRepository;
+import org.librecms.contentsection.ContentSection;
+import org.librecms.ui.contentsections.ContentSectionsUi;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+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 + "videoasset-edit-download")
+public class VideoAssetEditStepDownload {
+
+ @Inject
+ private AssetRepository assetRepo;
+
+ @Inject
+ private BinaryAssetDataService dataService;
+
+ @Inject
+ private ContentSectionsUi sectionsUi;
+
+ @GET
+ @Path("/")
+ @AuthorizationRequired
+ 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 VideoAsset)) {
+ 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 VideoAsset videoAsset = (VideoAsset) asset;
+ return Response
+ .ok()
+ .entity(
+ new StreamingOutput() {
+
+ @Override
+ public void write(final OutputStream outputStream)
+ throws IOException, WebApplicationException {
+ dataService.copyDataToOutputStream(
+ videoAsset, outputStream
+ );
+ }
+
+ })
+ .header("Content-Type", videoAsset.getMimeType())
+ .header(
+ "Content-Disposition",
+ String.format(
+ "attachment; filename=\"%s\"",
+ videoAsset.getFileName()
+ )
+ )
+ .build();
+ }
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/VideoAssetEditStepModel.java b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/VideoAssetEditStepModel.java
new file mode 100644
index 000000000..5965e6859
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/contentsections/assets/VideoAssetEditStepModel.java
@@ -0,0 +1,117 @@
+/*
+ * 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.librecms.assets.LegalMetadata;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Named;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Named("CmsVideoAssetEditStepModel")
+public class VideoAssetEditStepModel {
+
+ private Map descriptionValues;
+
+ private List unusedDescriptionLocales;
+
+ private String fileName;
+
+ private String mimeType;
+
+ private long size;
+
+ private String sizeLabel;
+
+ private LegalMetadata legalMetadata;
+
+ public Map getDescriptionValues() {
+ return Collections.unmodifiableMap(descriptionValues);
+ }
+
+ protected void setDescriptionValues(
+ final Map descriptionValues
+ ) {
+ this.descriptionValues = new HashMap<>(descriptionValues);
+ }
+
+ public List getUnusedDescriptionLocales() {
+ return Collections.unmodifiableList(unusedDescriptionLocales);
+ }
+
+ protected void setUnusedDescriptionLocales(
+ final List descriptionLocales
+ ) {
+ this.unusedDescriptionLocales = new ArrayList<>(descriptionLocales);
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ protected void setFileName(final String fileName) {
+ this.fileName = fileName;
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ protected void setMimeType(final String mimeType) {
+ this.mimeType = mimeType;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ protected void setSize(final long size) {
+ this.size = size;
+ }
+
+ public String getSizeLabel() {
+ return sizeLabel;
+ }
+
+ protected void setSizeLabel(final String sizeLabel) {
+ this.sizeLabel = sizeLabel;
+ }
+
+ public LegalMetadata getLegalMetadata() {
+ return legalMetadata;
+ }
+
+ protected void setLegalMetadata(final LegalMetadata legalMetadata) {
+ this.legalMetadata = legalMetadata;
+ }
+
+ public String getLegalMetadataType() {
+ return LegalMetadata.class.getName();
+ }
+}
diff --git a/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle.properties b/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle.properties
index 28d309aa0..68f022968 100644
--- a/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle.properties
+++ b/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle.properties
@@ -388,3 +388,15 @@ createform.externalvideoasset.url.label=URL
createform.externalvideoasset.description.help=A short description of the contents of the video.
createform.externalvideoasset.description.label=Description
fileasset.editstep.file.download.button.label=Download File
+image.description=An asset for managing images.
+image.label=Image
+image.editstep.label=Edit image
+image.editstep.description=Edit image
+audioasset.description=Asset for managing audio files
+audioasset.label=Audio
+videoasset.label=Video
+videoasset.description=An asset for videos.
+audioasset.editstep.label=Edit audio asset
+audioasset.editstep.description=Edit audio asset
+videoasset.editstep.description=Edit video asset
+videoasset.editstep.label=Edit video asset
diff --git a/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle_de.properties b/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle_de.properties
index 669d21c13..49444db44 100644
--- a/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle_de.properties
+++ b/ccm-cms/src/main/resources/org/librecms/ui/MvcAssetStepsBundle_de.properties
@@ -388,3 +388,15 @@ createform.externalvideoasset.url.label=URL
createform.externalvideoasset.description.help=Eine kurze Beschreibung des Inhaltes des Videos.
createform.externalvideoasset.description.label=Beschreibung
fileasset.editstep.file.download.button.label=Datei herunterladen
+image.description=Asset f\u00fcr Bilder.
+image.label=Bild
+image.editstep.label=Bild bearbeiten
+image.editstep.description=Bild bearbeiten
+audioasset.description=Asset f\u00fcr Audio-Dateien
+audioasset.label=Audio
+videoasset.label=Video
+videoasset.description=Asset f\u00fcr Videos.
+audioasset.editstep.label=Audio Asset bearbeiten
+audioasset.editstep.description=Audio Asset bearbeiten
+videoasset.editstep.description=Video Asset bearbeiten
+videoasset.editstep.label=Video Asset bearbeiten