Additional service/endpoint for retrieving file content from a binary asset.
parent
618893a41f
commit
6c18304cd2
|
|
@ -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;
|
||||||
|
|
@ -129,8 +198,8 @@ public class BinaryAsset extends Asset implements Serializable {
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue