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 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 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 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