Additional service/endpoint for retrieving file content from a binary asset.

pull/10/head
Jens Pelzetter 2021-10-18 21:22:59 +02:00
parent 618893a41f
commit 6c18304cd2
11 changed files with 681 additions and 64 deletions

View File

@ -38,6 +38,7 @@ import org.hibernate.envers.NotAudited;
import org.libreccm.core.UnexpectedErrorException; import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.jpa.utils.MimeTypeConverter; import org.libreccm.jpa.utils.MimeTypeConverter;
import org.libreccm.l10n.LocalizedString; import org.libreccm.l10n.LocalizedString;
import org.librecms.contentsection.privileges.AssetPrivileges;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -47,7 +48,8 @@ import java.sql.SQLException;
import javax.persistence.Basic; import javax.persistence.Basic;
import javax.persistence.Convert; import javax.persistence.Convert;
import javax.persistence.FetchType; import javax.persistence.FetchType;
import javax.transaction.Transactional; import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import static org.librecms.CmsConstants.*; import static org.librecms.CmsConstants.*;
@ -59,6 +61,73 @@ import static org.librecms.CmsConstants.*;
@Entity @Entity
@Table(name = "BINARY_ASSETS", schema = DB_SCHEMA) @Table(name = "BINARY_ASSETS", schema = DB_SCHEMA)
@Audited @Audited
@NamedQueries({
@NamedQuery(
name = "BinaryAsset.findById",
query = "SELECT DISTINCT a "
+ "FROM BinaryAsset a "
+ "LEFT JOIN a.permissions p "
+ "WHERE a.objectId = :assetId "
+ "LEFT JOIN a.permissions p "
+ "AND ("
+ " ("
+ " p.grantee IN :roles "
+ " AND p.grantedPrivilege = "
+ " '" + AssetPrivileges.VIEW + "' "
+ " ) "
+ " OR true = :isSystemUser OR true = :isAdmin"
+ ")"
),
@NamedQuery(
name = "BinaryAsset.findByUuid",
query = "SELECT DISTINCT a "
+ "FROM BinaryAsset a "
+ "LEFT JOIN a.permissions p "
+ "WHERE a.uuid = :uuid "
+ "AND ("
+ " ("
+ " p.grantee IN :roles "
+ " AND p.grantedPrivilege = "
+ " '" + AssetPrivileges.VIEW + "' "
+ " ) "
+ " OR true = :isSystemUser OR true = :isAdmin"
+ ")"
),
@NamedQuery(
name = "BinaryAsset.findByContentSection",
query = "SELECT DISTINCT a "
+ "FROM BinaryAsset a "
+ "JOIN a.categories c "
+ "LEFT JOIN a.permissions p "
+ "WHERE c.category.section = :section "
+ "AND ("
+ " ("
+ " p.grantee IN :roles "
+ " AND p.grantedPrivilege = "
+ " '" + AssetPrivileges.VIEW + "' "
+ " ) "
+ " OR true = :isSystemUser OR true = :isAdmin"
+ ")"
),
@NamedQuery(
name = "BinaryAsset.findByNameInFolder",
query = "SELECT DISTINCT a "
+ "FROM BinaryAsset a "
+ "JOIN a.categories c "
+ "LEFT JOIN a.permissions p "
+ "WHERE c.category = :folder "
+ "AND c.type = '" + CATEGORIZATION_TYPE_FOLDER + "' "
+ "AND a.displayName = :name "
+ "AND ("
+ " ("
+ " p.grantee IN :roles "
+ " AND p.grantedPrivilege = '"
+ AssetPrivileges.VIEW + "'"
+ " ) "
+ " OR true = :isSystemUser OR true = :isAdmin"
+ " )"
)
})
public class BinaryAsset extends Asset implements Serializable { public class BinaryAsset extends Asset implements Serializable {
private static final long serialVersionUID = -8540922051232103527L; private static final long serialVersionUID = -8540922051232103527L;
@ -126,11 +195,11 @@ public class BinaryAsset extends Asset implements Serializable {
public Blob getData() { public Blob getData() {
return data; return data;
} }
public long getDataSize() { public long getDataSize() {
try { try {
return data.length(); return data.length();
} catch(SQLException ex) { } catch (SQLException ex) {
throw new UnexpectedErrorException(ex); throw new UnexpectedErrorException(ex);
} }
} }

View File

@ -0,0 +1,387 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.assets;
import com.arsdigita.kernel.KernelConfig;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libreccm.auditing.AbstractAuditedEntityRepository;
import org.libreccm.categorization.Categorization;
import org.libreccm.categorization.Category;
import org.libreccm.categorization.CategoryManager;
import org.libreccm.categorization.ObjectNotAssignedToCategoryException;
import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.core.CcmObjectRepository;
import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.security.AuthorizationRequired;
import org.libreccm.security.Permission;
import org.libreccm.security.PermissionChecker;
import org.libreccm.security.PermissionManager;
import org.libreccm.security.RequiresPrivilege;
import org.libreccm.security.Role;
import org.libreccm.security.RoleManager;
import org.libreccm.security.Shiro;
import org.libreccm.security.User;
import org.libreccm.security.UserRepository;
import org.librecms.contentsection.Asset;
import org.librecms.contentsection.AssetInUseException;
import org.librecms.contentsection.AssetManager;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.Folder;
import org.librecms.contentsection.FolderRepository;
import org.librecms.contentsection.FolderType;
import org.librecms.contentsection.PathUtil;
import org.librecms.contentsection.privileges.AssetPrivileges;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import javax.transaction.Transactional;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
public class BinaryAssetRepository
extends AbstractAuditedEntityRepository<Long, BinaryAsset> {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LogManager
.getLogger(BinaryAssetRepository.class);
@Inject
private AssetManager assetManager;
@Inject
private CategoryManager categoryManager;
@Inject
private CcmObjectRepository ccmObjectRepo;
@Inject
private ConfigurationManager confManager;
@Inject
private EntityManager entityManager;
@Inject
private FolderRepository folderRepo;
@Inject
private PermissionChecker permissionChecker;
@Inject
private PermissionManager permissionManager;
@Inject
private RoleManager roleManager;
@Inject
private Shiro shiro;
@Inject
private UserRepository userRepository;
@Override
public Long getEntityId(final BinaryAsset entity) {
return entity.getObjectId();
}
@Override
public Class<BinaryAsset> getEntityClass() {
return BinaryAsset.class;
}
@Override
public String getIdAttributeName() {
return "objectId";
}
@Override
public Long getIdOfEntity(final BinaryAsset entity) {
return entity.getObjectId();
}
@Override
public boolean isNew(final BinaryAsset asset) {
return asset.getObjectId() == 0;
}
/**
* Set the UUID of a new asset.
*
* @param asset
*/
@Override
public void initNewEntity(final BinaryAsset asset) {
super.initNewEntity(asset);
if (asset.getUuid() == null) {
final String uuid = UUID.randomUUID().toString();
asset.setUuid(uuid);
}
}
@Transactional(Transactional.TxType.REQUIRED)
public Optional<BinaryAsset> findById(final long assetId) {
final TypedQuery<BinaryAsset> query = getEntityManager()
.createNamedQuery("BinaryAsset.findById", BinaryAsset.class)
.setParameter("assetId", assetId);
setAuthorizationParameters(query);
try {
return Optional.of(query.getSingleResult());
} catch (NoResultException ex) {
return Optional.empty();
}
}
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
@Override
public void save(
@RequiresPrivilege(AssetPrivileges.EDIT)
final BinaryAsset asset) {
super.save(asset);
}
/**
* Deletes an <strong>unused</strong> Asset. If the {@link Asset} is in use
* (linked to at least one ContentItem) an {@link AssetInUseException} is
* thrown. Use {@link AssetManager#isAssetInUse} to check if an
* {@link Asset} is used.
*
* @param asset The {@link Asset} to delete.
*
* @throws AssetInUseException if the {@link Asset} to delete is in use.
*/
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
@Override
public void delete(
@RequiresPrivilege(AssetPrivileges.DELETE)
final BinaryAsset asset
) {
if (assetManager.isAssetInUse(asset)) {
throw new AssetInUseException(
String.format(
"BinaryAsset %s is in use.",
asset.getUuid()
)
);
} else {
final List<Category> categories = asset.getCategories()
.stream()
.map(categorization -> categorization.getCategory())
.collect(Collectors.toList());
for (final Category category : categories) {
try {
categoryManager.removeObjectFromCategory(asset, category);
} catch (ObjectNotAssignedToCategoryException ex) {
throw new UnexpectedErrorException(ex);
}
}
final List<Permission> permissions = asset.getPermissions();
for (final Permission permission : permissions) {
permissionManager.revokePrivilege(permission
.getGrantedPrivilege(),
permission.getGrantee(),
asset);
}
ccmObjectRepo.delete(asset);
}
}
/**
* Find an {@link Asset} by its UUID. This method does not distinguish
* between shared and non shared assets.
*
* @param uuid The UUID of the {@link Asset}.
*
* @return An {@link Optional} containing the {@link Asset} with the
* provided {@code uuid} if there is an asset with that
* {@code uuid}. Otherwise an empty {@link Optional} is returned.
*/
@Transactional(Transactional.TxType.REQUIRED)
public Optional<BinaryAsset> findByUuid(final String uuid) {
final TypedQuery<BinaryAsset> query = entityManager
.createNamedQuery("BinaryAsset.findByUuid", BinaryAsset.class);
query.setParameter("uuid", uuid);
setAuthorizationParameters(query);
try {
return Optional.of(query.getSingleResult());
} catch (NoResultException ex) {
return Optional.empty();
}
}
@Transactional(Transactional.TxType.REQUIRED)
public List<BinaryAsset> findByContentSection(final ContentSection section) {
final TypedQuery<BinaryAsset> query = entityManager
.createNamedQuery(
"BinaryAsset.findByContentSection", BinaryAsset.class
);
query.setParameter("section", section);
setAuthorizationParameters(query);
return query.getResultList();
}
@Transactional(Transactional.TxType.REQUIRED)
public Optional<BinaryAsset> findByNameInFolder(
final Folder folder, final String name
) {
final TypedQuery<BinaryAsset> query = getEntityManager()
.createNamedQuery(
"BinaryAsset.findByNameInFolder", BinaryAsset.class
)
.setParameter("folder", folder)
.setParameter("name", name);
setAuthorizationParameters(query);
try {
return Optional.of(query.getSingleResult());
} catch (NoResultException ex) {
return Optional.empty();
}
}
@Transactional(Transactional.TxType.REQUIRED)
public Optional<BinaryAsset> findByPath(final String path) {
//The last token is the name of the asset itself. Remove this part and
//get the folder containing the asset using the FolderRepository.
final String normalizedPath = PathUtil.normalizePath(path);
final int lastTokenStart = normalizedPath.lastIndexOf('/');
final String folderPath = normalizedPath.substring(0, lastTokenStart);
final String assetName = normalizedPath.substring(lastTokenStart + 1);
final Optional<Folder> folder = folderRepo.findByPath(
folderPath, FolderType.ASSETS_FOLDER);
if (folder.isPresent()) {
return findByNameInFolder(folder.get(), assetName);
} else {
return Optional.empty();
}
}
@Transactional(Transactional.TxType.REQUIRED)
public Optional<BinaryAsset> findByPath(
final ContentSection section, final String path
) {
//The last token is the name of the asset itself. Remove this part an get
//the folder containing the asset using the FolderRepository.
final String normalizedPath = PathUtil.normalizePath(path);
final int lastTokenStart = normalizedPath.lastIndexOf('/');
final String assetName;
final Optional<Folder> folder;
if (lastTokenStart < 0) {
assetName = normalizedPath;
folder = folderRepo
.findById(section.getRootAssetsFolder().getObjectId());
} else {
final String folderPath = normalizedPath
.substring(0, lastTokenStart);
assetName = normalizedPath.substring(lastTokenStart + 1);
folder = folderRepo
.findByPath(section, folderPath, FolderType.ASSETS_FOLDER);
}
if (folder.isPresent()) {
LOGGER.debug("transaction is active? {}",
entityManager.isJoinedToTransaction());
LOGGER.debug("Folder for path {} found...", path);
// LOGGER.debug("Assets in the folder:");
// final Folder theFolder = folderRepo
// .findById(folder.get().getObjectId())
// .orElseThrow(() -> new IllegalArgumentException());
for (final Categorization categorization : folder.get().getObjects()) {
LOGGER.debug(" {}",
categorization.getCategorizedObject()
.getDisplayName());
}
return findByNameInFolder(folder.get(), assetName);
} else {
return Optional.empty();
}
}
private void setAuthorizationParameters(final TypedQuery<?> query) {
final Optional<User> user = shiro.getUser();
final List<Role> roles;
if (user.isPresent()) {
roles = user
.get()
.getRoleMemberships()
.stream()
.map(membership -> membership.getRole())
.collect(Collectors.toList());
} else {
final Optional<User> publicUser;
final KernelConfig kernelConfig = confManager
.findConfiguration(KernelConfig.class);
final String principal = (String) shiro
.getPublicUser()
.getPrincipal();
if (kernelConfig.emailIsPrimaryIdentifier()) {
publicUser = userRepository.findByEmailAddress(principal);
} else {
publicUser = userRepository.findByName(principal);
}
if (publicUser.isPresent()) {
roles = roleManager.findAllRolesForUser(publicUser.get());
} else {
roles = Collections.emptyList();
}
}
final boolean isSystemUser = shiro.isSystemUser();
final boolean isAdmin = permissionChecker.isPermitted("*");
query.setParameter("roles", roles);
query.setParameter("isSystemUser", isSystemUser);
query.setParameter("isAdmin", isAdmin);
}
}

View File

@ -41,7 +41,7 @@ public final class PathUtil {
* @param path The path to normalise. * @param path The path to normalise.
* @return The normalised path * @return The normalised path
*/ */
protected static final String normalizePath(final String path) { public static final String normalizePath(final String path) {
String normalizedPath = path; String normalizedPath = path;
if (normalizedPath.charAt(0) == '/') { if (normalizedPath.charAt(0) == '/') {
normalizedPath = normalizedPath.substring(1); normalizedPath = normalizedPath.substring(1);

View File

@ -163,7 +163,7 @@ public class Assets {
assets assets
.stream() .stream()
.map(this::assetToJson) .map(asset -> assetToJson(asset, contentSection))
.forEach(arrayBuilder::add); .forEach(arrayBuilder::add);
final StringWriter writer = new StringWriter(); final StringWriter writer = new StringWriter();
@ -189,7 +189,7 @@ public class Assets {
final Folder folder = contentSection.getRootAssetsFolder(); final Folder folder = contentSection.getRootAssetsFolder();
return findAssetsInFolder(folder, query, type); return findAssetsInFolder(contentSection, folder, query, type);
} }
@GET @GET
@ -215,13 +215,15 @@ public class Assets {
folderPath, folderPath,
section))); section)));
return findAssetsInFolder(folder, query, type); return findAssetsInFolder(contentSection, folder, query, type);
} }
private String findAssetsInFolder(final Folder folder, private String findAssetsInFolder(
final String query, final ContentSection section,
final String type) { final Folder folder,
final String query,
final String type
) {
final List<Asset> assets; final List<Asset> assets;
if ((query == null || query.trim().isEmpty()) if ((query == null || query.trim().isEmpty())
&& ((type == null) || type.trim().isEmpty())) { && ((type == null) || type.trim().isEmpty())) {
@ -245,12 +247,12 @@ public class Assets {
folder folder
.getSubFolders() .getSubFolders()
.stream() .stream()
.map(this::assetToJson) .map(subFolder -> assetToJson(subFolder, section))
.forEach(arrayBuilder::add); .forEach(arrayBuilder::add);
assets assets
.stream() .stream()
.map(this::assetToJson) .map(asset -> assetToJson(asset, section))
.forEach(arrayBuilder::add); .forEach(arrayBuilder::add);
final StringWriter writer = new StringWriter(); final StringWriter writer = new StringWriter();
@ -283,19 +285,25 @@ public class Assets {
} }
} }
private JsonObject assetToJson(final Folder folder) { private JsonObject assetToJson(
final Folder folder,
final ContentSection section
) {
return Json return Json
.createObjectBuilder() .createObjectBuilder()
.add("title", .add("title",
folder.getTitle().getValue(defaultLocale)) folder.getTitle().getValue(defaultLocale))
.add("type", Folder.class.getName()) .add("type", Folder.class.getName())
.add("section", section.getLabel())
.add("path", folderManager.getFolderPath(folder))
.add("place", "") .add("place", "")
.build(); .build();
} }
private JsonObject assetToJson(final Asset asset) { private JsonObject assetToJson(
final Asset asset,
final ContentSection section
) {
final AssetTypeInfo typeInfo = assetTypesManager final AssetTypeInfo typeInfo = assetTypesManager
.getAssetTypeInfo(asset.getClass()); .getAssetTypeInfo(asset.getClass());
final ResourceBundle bundle = ResourceBundle final ResourceBundle bundle = ResourceBundle
@ -318,6 +326,8 @@ public class Assets {
asset.getTitle())) asset.getTitle()))
.add("type", asset.getClass().getName()) .add("type", asset.getClass().getName())
.add("typeLabel", bundle.getString(typeInfo.getLabelKey())) .add("typeLabel", bundle.getString(typeInfo.getLabelKey()))
.add("contentsection", section.getLabel())
.add("path", assetManager.getAssetPath(asset))
.add("place", place) .add("place", place)
.add("properties", getAssetProperties(asset)) .add("properties", getAssetProperties(asset))
.build(); .build();

View File

@ -39,6 +39,7 @@ public class ContentSectionsApplication extends Application{
classes.add(Assets.class); classes.add(Assets.class);
classes.add(ContentItems.class); classes.add(ContentItems.class);
classes.add(ContentSections.class); classes.add(ContentSections.class);
classes.add(Files.class);
classes.add(Images.class); classes.add(Images.class);
return classes; return classes;

View File

@ -0,0 +1,150 @@
/*
* 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.contentsection.rs;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.librecms.assets.BinaryAsset;
import org.librecms.assets.BinaryAssetDataService;
import org.librecms.assets.BinaryAssetRepository;
import org.librecms.contentsection.Asset;
import org.librecms.contentsection.AssetRepository;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.ContentSectionRepository;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Optional;
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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Path("/{content-section}/files")
public class Files {
private static final Logger LOGGER = LogManager.getLogger(Files.class);
// @Inject
// private AssetRepository assetRepo;
@Inject
private BinaryAssetRepository binaryAssetRepo;
@Inject
private BinaryAssetDataService dataService;
@Inject
private ContentSectionRepository sectionRepo;
@GET
@Path("/uuid-{uuid}")
public Response getFileByUuid(
@PathParam("content-section") final String sectionName,
@PathParam("uuid") final String uuid
) {
final Optional<BinaryAsset> asset = binaryAssetRepo.findByUuid(uuid);
if (asset.isPresent()) {
return loadFile(asset.get());
} else {
return Response
.status(Response.Status.NOT_FOUND)
.entity(
String.format(
"The requested file \"%s\" does not exist.",
uuid
)
)
.build();
}
}
@GET
@Path("/{path:^(?!uuid).+$}")
public Response getFile(
@PathParam("content-section") final String sectionName,
@PathParam("path") final String path
) {
final Optional<ContentSection> section = sectionRepo
.findByLabel(sectionName);
if (!section.isPresent()) {
return Response
.status(Response.Status.NOT_FOUND)
.entity(String.format("No content section \"%s\" available.",
sectionName))
.build();
}
final Optional<BinaryAsset> asset = binaryAssetRepo.findByPath(
section.get(), path
);
if (asset.isPresent()) {
return loadFile(asset.get());
} else {
return Response
.status(Response.Status.NOT_FOUND)
.entity(
String.format(
"The requested file \"%s\" does not exist.",
path
)
)
.build();
}
}
private Response loadFile(final BinaryAsset asset) {
return Response
.ok()
.entity(
new StreamingOutput() {
@Override
public void write(final OutputStream outputStream)
throws IOException, WebApplicationException {
dataService.copyDataToOutputStream(asset, outputStream);
}
})
.header("ContentType", asset.getMimeType())
.header(
"Content-Disposition",
String.format(
"attachment; filename=\"%s\"",
asset.getFileName()
)
)
.build();
}
}

View File

@ -27,7 +27,6 @@ import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.ContentSectionRepository; import org.librecms.contentsection.ContentSectionRepository;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -65,17 +64,12 @@ public class Images {
@GET @GET
@Path("/uuid-{uuid}") @Path("/uuid-{uuid}")
public Response getImageByUuid( public Response getImageByUuid(
@PathParam("content-section") @PathParam("content-section") final String sectionName,
final String sectionName, @PathParam("uuid") final String uuid,
@PathParam("uuid") @QueryParam("width") @DefaultValue("-1")
final String uuid, final String widthParam, @QueryParam("height")
@QueryParam("width") @DefaultValue("-1") final String heightParam
@DefaultValue("-1") ) {
final String widthParam,
@QueryParam("height")
@DefaultValue("-1")
final String heightParam) {
final Optional<Image> asset = assetRepo final Optional<Image> asset = assetRepo
.findByUuidAndType(uuid, Image.class); .findByUuidAndType(uuid, Image.class);
@ -84,9 +78,12 @@ public class Images {
} else { } else {
return Response return Response
.status(Response.Status.NOT_FOUND) .status(Response.Status.NOT_FOUND)
.entity(String .entity(
.format("The requested image \"%s\" does not exist.", String.format(
uuid)) "The requested image \"%s\" does not exist.",
uuid
)
)
.build(); .build();
} }
} }
@ -95,8 +92,8 @@ public class Images {
@Path("/uuid-{uuid}/properties") @Path("/uuid-{uuid}/properties")
public Response getImagePropertiesByUuid( public Response getImagePropertiesByUuid(
@PathParam("content-section") final String sectionName, @PathParam("content-section") final String sectionName,
@PathParam("uuid") final String uuid) { @PathParam("uuid") final String uuid
) {
final Optional<Image> asset = assetRepo.findByUuidAndType( final Optional<Image> asset = assetRepo.findByUuidAndType(
uuid, Image.class uuid, Image.class
); );
@ -144,17 +141,11 @@ public class Images {
@GET @GET
@Path("/{path:^(?!uuid).+$}") @Path("/{path:^(?!uuid).+$}")
public Response getImage( public Response getImage(
@PathParam("content-section") @PathParam("content-section") final String sectionName,
final String sectionName, @PathParam("path") final String path,
@PathParam("path") @QueryParam("width") @DefaultValue("-1") final String widthParam,
final String path, @QueryParam("height") @DefaultValue("-1") final String heightParam
@QueryParam("width") ) {
@DefaultValue("-1")
final String widthParam,
@QueryParam("height")
@DefaultValue("-1")
final String heightParam) {
final Optional<ContentSection> section = sectionRepo final Optional<ContentSection> section = sectionRepo
.findByLabel(sectionName); .findByLabel(sectionName);
if (!section.isPresent()) { if (!section.isPresent()) {
@ -165,8 +156,9 @@ public class Images {
.build(); .build();
} }
final Optional<Asset> asset = assetRepo.findByPath(section.get(), final Optional<Asset> asset = assetRepo.findByPath(
path); section.get(), path
);
if (asset.isPresent()) { if (asset.isPresent()) {
if (asset.get() instanceof Image) { if (asset.get() instanceof Image) {
@ -174,18 +166,24 @@ public class Images {
} else { } else {
return Response return Response
.status(Response.Status.NOT_FOUND) .status(Response.Status.NOT_FOUND)
.entity(String .entity(
.format("The asset found at the requested path \"%s\" " String.format(
+ "is not an image.", "The asset found at the requested path \"%s\" "
path)) + "is not an image.",
path
)
)
.build(); .build();
} }
} else { } else {
return Response return Response
.status(Response.Status.NOT_FOUND) .status(Response.Status.NOT_FOUND)
.entity(String .entity(
.format("The requested image \"%s\" does not exist.", String.format(
path)) "The requested image \"%s\" does not exist.",
path
)
)
.build(); .build();
} }
} }
@ -253,10 +251,11 @@ public class Images {
* @return The {@link Response} for sending the (scaled) image to the * @return The {@link Response} for sending the (scaled) image to the
* requesting user agent. * requesting user agent.
*/ */
private Response loadImage(final Image image, private Response loadImage(
final String widthParam, final Image image,
final String heightParam) { final String widthParam,
final String heightParam
) {
//final byte[] data = image.getData(); //final byte[] data = image.getData();
final String mimeType = image.getMimeType().toString(); final String mimeType = image.getMimeType().toString();

View File

@ -119,7 +119,9 @@ public class FileAssetEditStepDownload {
@Override @Override
public void write(final OutputStream outputStream) public void write(final OutputStream outputStream)
throws IOException, WebApplicationException { throws IOException, WebApplicationException {
dataService.copyDataToOutputStream(fileAsset, outputStream); dataService.copyDataToOutputStream(
fileAsset, outputStream
);
} }
}) })

View File

@ -28,7 +28,6 @@ import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.l10n.GlobalizationHelper; import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.security.AuthorizationRequired; import org.libreccm.security.AuthorizationRequired;
import org.librecms.assets.BinaryAssetDataService; import org.librecms.assets.BinaryAssetDataService;
import org.librecms.assets.FileAsset;
import org.librecms.assets.VideoAsset; import org.librecms.assets.VideoAsset;
import org.librecms.assets.LegalMetadata; import org.librecms.assets.LegalMetadata;
import org.librecms.contentsection.AssetRepository; import org.librecms.contentsection.AssetRepository;
@ -72,7 +71,7 @@ import javax.ws.rs.core.MultivaluedMap;
bundle = MvcAssetStepsConstants.BUNDLE, bundle = MvcAssetStepsConstants.BUNDLE,
descriptionKey = "videoasset.editstep.description", descriptionKey = "videoasset.editstep.description",
labelKey = "videoasset.editstep.lable", labelKey = "videoasset.editstep.lable",
supportedAssetType = FileAsset.class supportedAssetType = VideoAsset.class
) )
public class VideoAssetEditStep extends AbstractMvcAssetEditStep { public class VideoAssetEditStep extends AbstractMvcAssetEditStep {

View File

@ -872,7 +872,7 @@
<figure> <figure>
<video controls="controls" <video controls="controls"
src="" src=""
style="min-heigth: 4em;"> style="min-height: 4em;">
#{CmsAdminMessages['cms_editor.video_node_view.video.none']} #{CmsAdminMessages['cms_editor.video_node_view.video.none']}
</video> </video>
<div class="border-light librecms-video-node-view-buttons"> <div class="border-light librecms-video-node-view-buttons">

View File

@ -462,7 +462,7 @@ function loadVideos(
selectButton.addEventListener( selectButton.addEventListener(
"click", "click",
(event) => { (event) => {
const videoUrl = `/content-sections/info/videos/uuid-${video["uuid"]}`; const videoUrl = `/@contentsections/info/videos/uuid-${video["uuid"]}`;
node.attrs.videoSrc = videoUrl; node.attrs.videoSrc = videoUrl;
if (videoElem) { if (videoElem) {
videoElem.src = videoUrl; videoElem.src = videoUrl;