CCM NG/ccm-cms: Thumbnails for images in AssetBrowser

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4888 8810af33-2d31-482b-a856-94f89814c4df
jensp 2017-07-27 10:26:14 +00:00
parent f1c033c307
commit f7d7293868
10 changed files with 418 additions and 272 deletions

View File

@ -101,6 +101,8 @@ public class AssetFolderBrowser extends Table {
CMS_FOLDER_BUNDLE), CMS_FOLDER_BUNDLE),
new GlobalizedMessage("cms.ui.folder.type", new GlobalizedMessage("cms.ui.folder.type",
CMS_FOLDER_BUNDLE), CMS_FOLDER_BUNDLE),
new GlobalizedMessage("cms.ui.asset.thumbnail",
CMS_BUNDLE),
new GlobalizedMessage("cms.ui.folder.creation_date", new GlobalizedMessage("cms.ui.folder.creation_date",
CMS_FOLDER_BUNDLE), CMS_FOLDER_BUNDLE),
new GlobalizedMessage("cms.ui.folder.last_modified", new GlobalizedMessage("cms.ui.folder.last_modified",
@ -120,6 +122,9 @@ public class AssetFolderBrowser extends Table {
nameColumn.setCellRenderer(new NameCellRenderer()); nameColumn.setCellRenderer(new NameCellRenderer());
nameColumn.setHeaderRenderer(new HeaderCellRenderer(SORT_KEY_NAME)); nameColumn.setHeaderRenderer(new HeaderCellRenderer(SORT_KEY_NAME));
getColumn(AssetFolderBrowserTableModel.COL_THUMBNAIL)
.setCellRenderer(new ThumbnailCellRenderer());
getColumn(AssetFolderBrowserTableModel.COL_CREATION_DATE) getColumn(AssetFolderBrowserTableModel.COL_CREATION_DATE)
.setHeaderRenderer( .setHeaderRenderer(
new HeaderCellRenderer(SORT_KEY_CREATION_DATE)); new HeaderCellRenderer(SORT_KEY_CREATION_DATE));
@ -305,6 +310,26 @@ public class AssetFolderBrowser extends Table {
} }
private class ThumbnailCellRenderer implements TableCellRenderer {
@Override
public Component getComponent(final Table table,
final PageState state,
final Object value,
final boolean isSelected,
final Object key,
final int row,
final int column) {
if (value == null) {
return new Text("");
} else {
final Image image = new Image((String) value, "");
return image;
}
}
}
private class DateCellRenderer implements TableCellRenderer { private class DateCellRenderer implements TableCellRenderer {
@Override @Override

View File

@ -18,7 +18,9 @@
*/ */
package com.arsdigita.cms.ui.assets; package com.arsdigita.cms.ui.assets;
import com.arsdigita.cms.CMS;
import com.arsdigita.kernel.KernelConfig; import com.arsdigita.kernel.KernelConfig;
import com.arsdigita.web.CCMDispatcherServlet;
import org.libreccm.categorization.Category; import org.libreccm.categorization.Category;
import org.libreccm.categorization.CategoryManager; import org.libreccm.categorization.CategoryManager;
@ -58,8 +60,10 @@ import org.librecms.contentsection.FolderRepository;
import java.util.Collections; import java.util.Collections;
import java.util.Optional; import java.util.Optional;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.librecms.assets.Image;
import static org.librecms.CmsConstants.*; import static org.librecms.CmsConstants.*;
@ -71,7 +75,7 @@ import static org.librecms.CmsConstants.*;
public class AssetFolderBrowserController { public class AssetFolderBrowserController {
private static final Logger LOGGER = LogManager private static final Logger LOGGER = LogManager
.getLogger(AssetFolderBrowserController.class); .getLogger(AssetFolderBrowserController.class);
@Inject @Inject
private EntityManager entityManager; private EntityManager entityManager;
@ -111,7 +115,7 @@ public class AssetFolderBrowserController {
@PostConstruct @PostConstruct
private void init() { private void init() {
final KernelConfig kernelConfig = confManager.findConfiguration( final KernelConfig kernelConfig = confManager.findConfiguration(
KernelConfig.class); KernelConfig.class);
defaultLocale = kernelConfig.getDefaultLocale(); defaultLocale = kernelConfig.getDefaultLocale();
} }
@ -128,9 +132,9 @@ public class AssetFolderBrowserController {
firstResult, firstResult,
maxResults); maxResults);
final List<AssetFolderBrowserTableRow> subFolderRows = subFolders final List<AssetFolderBrowserTableRow> subFolderRows = subFolders
.stream() .stream()
.map(subFolder -> buildRow(subFolder)) .map(subFolder -> buildRow(subFolder))
.collect(Collectors.toList()); .collect(Collectors.toList());
if (subFolders.size() > maxResults) { if (subFolders.size() > maxResults) {
return subFolderRows; return subFolderRows;
@ -145,9 +149,9 @@ public class AssetFolderBrowserController {
firstAsset, firstAsset,
maxAssets); maxAssets);
final List<AssetFolderBrowserTableRow> assetRows = assets final List<AssetFolderBrowserTableRow> assetRows = assets
.stream() .stream()
.map(asset -> buildRow(asset)) .map(asset -> buildRow(asset))
.collect(Collectors.toList()); .collect(Collectors.toList());
final List<AssetFolderBrowserTableRow> rows = new ArrayList<>(); final List<AssetFolderBrowserTableRow> rows = new ArrayList<>();
rows.addAll(subFolderRows); rows.addAll(subFolderRows);
@ -177,19 +181,19 @@ public class AssetFolderBrowserController {
criteriaQuery = criteriaQuery.select(builder.count(from)); criteriaQuery = criteriaQuery.select(builder.count(from));
final List<Folder> subFolders = findSubFolders( final List<Folder> subFolders = findSubFolders(
folder, folder,
filterTerm, filterTerm,
AssetFolderBrowser.SORT_KEY_NAME, AssetFolderBrowser.SORT_KEY_NAME,
AssetFolderBrowser.SORT_ACTION_UP, AssetFolderBrowser.SORT_ACTION_UP,
-1, -1,
-1); -1);
final List<Asset> assets = findAssetsInFolder( final List<Asset> assets = findAssetsInFolder(
folder, folder,
filterTerm, filterTerm,
AssetFolderBrowser.SORT_KEY_NAME, AssetFolderBrowser.SORT_KEY_NAME,
AssetFolderBrowser.SORT_ACTION_UP, AssetFolderBrowser.SORT_ACTION_UP,
-1, -1,
-1); -1);
if (subFolders.isEmpty() && assets.isEmpty()) { if (subFolders.isEmpty() && assets.isEmpty()) {
return 0; return 0;
@ -199,8 +203,8 @@ public class AssetFolderBrowserController {
criteriaQuery = criteriaQuery.where(from.in(subFolders)); criteriaQuery = criteriaQuery.where(from.in(subFolders));
} else { } else {
criteriaQuery = criteriaQuery.where(builder.or( criteriaQuery = criteriaQuery.where(builder.or(
from.in(subFolders), from.in(subFolders),
from.in(assets))); from.in(assets)));
} }
return entityManager.createQuery(criteriaQuery).getSingleResult(); return entityManager.createQuery(criteriaQuery).getSingleResult();
@ -218,17 +222,17 @@ public class AssetFolderBrowserController {
if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) { if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
copyFolder(targetFolder, copyFolder(targetFolder,
Long.parseLong(objectId.substring( Long.parseLong(objectId.substring(
FOLDER_BROWSER_KEY_PREFIX_FOLDER.length()))); FOLDER_BROWSER_KEY_PREFIX_FOLDER.length())));
} else if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_ASSET)) { } else if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_ASSET)) {
copyAsset(targetFolder, copyAsset(targetFolder,
Long.parseLong(objectId.substring( Long.parseLong(objectId.substring(
FOLDER_BROWSER_KEY_PREFIX_ASSET.length()))); FOLDER_BROWSER_KEY_PREFIX_ASSET.length())));
} else { } else {
throw new IllegalArgumentException(String.format( throw new IllegalArgumentException(String.format(
"ID '%s' does not start with '%s' or '%s'.", "ID '%s' does not start with '%s' or '%s'.",
objectId, objectId,
FOLDER_BROWSER_KEY_PREFIX_FOLDER, FOLDER_BROWSER_KEY_PREFIX_FOLDER,
FOLDER_BROWSER_KEY_PREFIX_ASSET)); FOLDER_BROWSER_KEY_PREFIX_ASSET));
} }
} }
@ -240,10 +244,10 @@ public class AssetFolderBrowserController {
Objects.requireNonNull(targetFolder); Objects.requireNonNull(targetFolder);
final Folder folder = folderRepo.findById(folderId) final Folder folder = folderRepo.findById(folderId)
.orElseThrow(() -> new IllegalArgumentException(String.format( .orElseThrow(() -> new IllegalArgumentException(String.format(
"No folder with ID %d in the database. " "No folder with ID %d in the database. "
+ "Where did that ID come from?", + "Where did that ID come from?",
folderId))); folderId)));
folderManager.copyFolder(folder, targetFolder); folderManager.copyFolder(folder, targetFolder);
@ -255,10 +259,10 @@ public class AssetFolderBrowserController {
Objects.requireNonNull(targetFolder); Objects.requireNonNull(targetFolder);
final Asset asset = assetRepo final Asset asset = assetRepo
.findById(assetId) .findById(assetId)
.orElseThrow(() -> new IllegalArgumentException(String.format( .orElseThrow(() -> new IllegalArgumentException(String.format(
"No asset ith ID %d in the database. Where did that ID come from?", "No asset ith ID %d in the database. Where did that ID come from?",
assetId))); assetId)));
assetManager.copy(asset, targetFolder); assetManager.copy(asset, targetFolder);
} }
@ -274,17 +278,17 @@ public class AssetFolderBrowserController {
if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) { if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
moveFolder(targetFolder, moveFolder(targetFolder,
Long.parseLong(objectId.substring( Long.parseLong(objectId.substring(
FOLDER_BROWSER_KEY_PREFIX_FOLDER.length()))); FOLDER_BROWSER_KEY_PREFIX_FOLDER.length())));
} else if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_ASSET)) { } else if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_ASSET)) {
moveAsset(targetFolder, moveAsset(targetFolder,
Long.parseLong(objectId.substring( Long.parseLong(objectId.substring(
FOLDER_BROWSER_KEY_PREFIX_ASSET.length()))); FOLDER_BROWSER_KEY_PREFIX_ASSET.length())));
} else { } else {
throw new IllegalArgumentException(String.format( throw new IllegalArgumentException(String.format(
"ID '%s' does not start with '%s' or '%s'.", "ID '%s' does not start with '%s' or '%s'.",
objectId, objectId,
FOLDER_BROWSER_KEY_PREFIX_FOLDER, FOLDER_BROWSER_KEY_PREFIX_FOLDER,
FOLDER_BROWSER_KEY_PREFIX_ASSET)); FOLDER_BROWSER_KEY_PREFIX_ASSET));
} }
} }
} }
@ -294,10 +298,10 @@ public class AssetFolderBrowserController {
Objects.requireNonNull(targetFolder); Objects.requireNonNull(targetFolder);
final Folder folder = folderRepo.findById(folderId) final Folder folder = folderRepo.findById(folderId)
.orElseThrow(() -> new IllegalArgumentException(String.format( .orElseThrow(() -> new IllegalArgumentException(String.format(
"No folder with ID %d in the database. " "No folder with ID %d in the database. "
+ "Where did that ID come from?", + "Where did that ID come from?",
folderId))); folderId)));
folderManager.moveFolder(folder, targetFolder); folderManager.moveFolder(folder, targetFolder);
} }
@ -307,10 +311,10 @@ public class AssetFolderBrowserController {
Objects.requireNonNull(targetFolder); Objects.requireNonNull(targetFolder);
final Asset asset = assetRepo final Asset asset = assetRepo
.findById(assetId) .findById(assetId)
.orElseThrow(() -> new IllegalArgumentException(String.format( .orElseThrow(() -> new IllegalArgumentException(String.format(
"No asset with ID %d in the database. Where did that ID come from?", "No asset with ID %d in the database. Where did that ID come from?",
assetId))); assetId)));
assetManager.move(asset, targetFolder); assetManager.move(asset, targetFolder);
} }
@ -321,20 +325,20 @@ public class AssetFolderBrowserController {
Objects.requireNonNull(sources); Objects.requireNonNull(sources);
final List<String> sourceFolderIds = sources final List<String> sourceFolderIds = sources
.stream() .stream()
.filter(source -> source.startsWith( .filter(source -> source.startsWith(
FOLDER_BROWSER_KEY_PREFIX_FOLDER)) FOLDER_BROWSER_KEY_PREFIX_FOLDER))
.collect(Collectors.toList()); .collect(Collectors.toList());
final List<String> parentFolderIds = sourceFolderIds final List<String> parentFolderIds = sourceFolderIds
.stream() .stream()
.map(sourceFolderId -> findParentFolderId(sourceFolderId)) .map(sourceFolderId -> findParentFolderId(sourceFolderId))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.collect(Collectors.toList()); .collect(Collectors.toList());
final List<List<String>> subFolderIds = sourceFolderIds final List<List<String>> subFolderIds = sourceFolderIds
.stream() .stream()
.map(sourceFolderId -> findSubFolderIds(sourceFolderId)) .map(sourceFolderId -> findSubFolderIds(sourceFolderId))
.collect(Collectors.toList()); .collect(Collectors.toList());
final List<String> invalidTargetIds = new ArrayList<>(); final List<String> invalidTargetIds = new ArrayList<>();
invalidTargetIds.addAll(sourceFolderIds); invalidTargetIds.addAll(sourceFolderIds);
@ -353,26 +357,26 @@ public class AssetFolderBrowserController {
if (!folderId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) { if (!folderId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
throw new IllegalArgumentException(String.format( throw new IllegalArgumentException(String.format(
"Provided string '%s' is not an ID of a folder.", "Provided string '%s' is not an ID of a folder.",
folderId)); folderId));
} }
final long objectId = Long.parseLong(folderId.substring( final long objectId = Long.parseLong(folderId.substring(
FOLDER_BROWSER_KEY_PREFIX_FOLDER.length())); FOLDER_BROWSER_KEY_PREFIX_FOLDER.length()));
final Folder folder = folderRepo.findById(objectId) final Folder folder = folderRepo.findById(objectId)
.orElseThrow(() -> new IllegalArgumentException(String.format( .orElseThrow(() -> new IllegalArgumentException(String.format(
"No folder with ID %d found in database. " "No folder with ID %d found in database. "
+ "Where did that ID come form?", + "Where did that ID come form?",
objectId))); objectId)));
final Optional<Folder> parentFolder = folderManager.getParentFolder( final Optional<Folder> parentFolder = folderManager.getParentFolder(
folder); folder);
if (parentFolder.isPresent()) { if (parentFolder.isPresent()) {
return Optional.empty(); return Optional.empty();
} else { } else {
return Optional.ofNullable(String.format( return Optional.ofNullable(String.format(
"%s%d", "%s%d",
FOLDER_BROWSER_KEY_PREFIX_FOLDER, FOLDER_BROWSER_KEY_PREFIX_FOLDER,
parentFolder.get().getObjectId())); parentFolder.get().getObjectId()));
} }
} }
@ -382,23 +386,23 @@ public class AssetFolderBrowserController {
if (!folderId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) { if (!folderId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
throw new IllegalArgumentException(String.format( throw new IllegalArgumentException(String.format(
"Provided string '%s' is not the ID of a folder.", "Provided string '%s' is not the ID of a folder.",
folderId)); folderId));
} }
final long objectId = Long.parseLong(folderId.substring( final long objectId = Long.parseLong(folderId.substring(
FOLDER_BROWSER_KEY_PREFIX_FOLDER.length())); FOLDER_BROWSER_KEY_PREFIX_FOLDER.length()));
final Folder folder = folderRepo.findById(objectId) final Folder folder = folderRepo.findById(objectId)
.orElseThrow(() -> new IllegalArgumentException(String.format( .orElseThrow(() -> new IllegalArgumentException(String.format(
"No folder with ID %d found in database. " "No folder with ID %d found in database. "
+ "Where did that ID come form?", + "Where did that ID come form?",
objectId))); objectId)));
return findSubFolders(folder) return findSubFolders(folder)
.stream() .stream()
.map(subFolder -> String.format("%s%d", .map(subFolder -> String.format("%s%d",
FOLDER_BROWSER_KEY_PREFIX_FOLDER, FOLDER_BROWSER_KEY_PREFIX_FOLDER,
subFolder.getObjectId())) subFolder.getObjectId()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private List<Folder> findSubFolders(final Folder folder) { private List<Folder> findSubFolders(final Folder folder) {
@ -406,7 +410,7 @@ public class AssetFolderBrowserController {
Objects.requireNonNull(folder); Objects.requireNonNull(folder);
if (folder.getSubFolders() == null if (folder.getSubFolders() == null
|| folder.getSubFolders().isEmpty()) { || folder.getSubFolders().isEmpty()) {
return Collections.emptyList(); return Collections.emptyList();
} }
@ -431,21 +435,21 @@ public class AssetFolderBrowserController {
if (objectId.startsWith("folder-")) { if (objectId.startsWith("folder-")) {
final long folderId = Long.parseLong( final long folderId = Long.parseLong(
objectId.substring("folder-".length())); objectId.substring("folder-".length()));
folderRepo folderRepo
.findById(folderId) .findById(folderId)
.ifPresent(folderRepo::delete); .ifPresent(folderRepo::delete);
} else if (objectId.startsWith("asset-")) { } else if (objectId.startsWith("asset-")) {
final long assetId = Long.parseLong( final long assetId = Long.parseLong(
objectId.substring("asset-".length())); objectId.substring("asset-".length()));
assetRepo assetRepo
.findById(assetId) .findById(assetId)
.ifPresent(assetRepo::delete); .ifPresent(assetRepo::delete);
} else { } else {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"The objectId is expected to start with 'folder-' or 'asset-'."); "The objectId is expected to start with 'folder-' or 'asset-'.");
} }
} }
@ -457,15 +461,15 @@ public class AssetFolderBrowserController {
row.setObjectUuid(folder.getUuid()); row.setObjectUuid(folder.getUuid());
row.setName(folder.getName()); row.setName(folder.getName());
if (folder.getTitle().hasValue(globalizationHelper if (folder.getTitle().hasValue(globalizationHelper
.getNegotiatedLocale())) { .getNegotiatedLocale())) {
row.setTitle(folder.getTitle().getValue(globalizationHelper row.setTitle(folder.getTitle().getValue(globalizationHelper
.getNegotiatedLocale())); .getNegotiatedLocale()));
} else { } else {
row.setTitle(folder.getTitle().getValue(defaultLocale)); row.setTitle(folder.getTitle().getValue(defaultLocale));
} }
row.setFolder(true); row.setFolder(true);
row.setDeletable(!categoryManager.hasSubCategories(folder) row.setDeletable(!categoryManager.hasSubCategories(folder)
&& !categoryManager.hasObjects(folder)); && !categoryManager.hasObjects(folder));
return row; return row;
} }
@ -478,14 +482,22 @@ public class AssetFolderBrowserController {
row.setObjectUuid(asset.getUuid()); row.setObjectUuid(asset.getUuid());
row.setName(asset.getDisplayName()); row.setName(asset.getDisplayName());
if (asset.getTitle().hasValue(globalizationHelper if (asset.getTitle().hasValue(globalizationHelper
.getNegotiatedLocale())) { .getNegotiatedLocale())) {
row.setTitle(asset.getTitle().getValue(globalizationHelper row.setTitle(asset.getTitle().getValue(globalizationHelper
.getNegotiatedLocale())); .getNegotiatedLocale()));
} else { } else {
row.setTitle(asset.getTitle().getValue(defaultLocale)); row.setTitle(asset.getTitle().getValue(defaultLocale));
} }
if (asset instanceof Image) {
row.setThumbnailUrl(String
.format("%s/content-sections/%s/images/"
+ "uuid-%s?width=150&height=100",
CCMDispatcherServlet.getContextPath(),
CMS.getContext().getContentSection().getLabel(),
asset.getUuid()));
}
final AssetTypeInfo typeInfo = typesManager final AssetTypeInfo typeInfo = typesManager
.getAssetTypeInfo(asset.getClass()); .getAssetTypeInfo(asset.getClass());
row.setTypeLabelBundle(typeInfo.getLabelBundle()); row.setTypeLabelBundle(typeInfo.getLabelBundle());
row.setTypeLabelKey(typeInfo.getLabelKey()); row.setTypeLabelKey(typeInfo.getLabelKey());
@ -506,32 +518,32 @@ public class AssetFolderBrowserController {
final CriteriaBuilder builder = entityManager.getCriteriaBuilder(); final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Folder> criteria = builder final CriteriaQuery<Folder> criteria = builder
.createQuery(Folder.class); .createQuery(Folder.class);
final Root<Folder> from = criteria.from(Folder.class); final Root<Folder> from = criteria.from(Folder.class);
final Order order; final Order order;
if (AssetFolderBrowser.SORT_KEY_NAME.equals(orderBy) if (AssetFolderBrowser.SORT_KEY_NAME.equals(orderBy)
&& AssetFolderBrowser.SORT_ACTION_DOWN. && AssetFolderBrowser.SORT_ACTION_DOWN.
equals(orderDirection)) { equals(orderDirection)) {
order = builder.desc(from.get("name")); order = builder.desc(from.get("name"));
} else { } else {
order = builder.asc(from.get("name")); order = builder.asc(from.get("name"));
} }
final TypedQuery<Folder> query = entityManager final TypedQuery<Folder> query = entityManager
.createQuery( .createQuery(
criteria.where( criteria.where(
builder.and( builder.and(
builder. builder.
equal(from.get("parentCategory"), equal(from.get("parentCategory"),
folder), folder),
builder.like(builder.lower(from.get( builder.like(builder.lower(from.get(
"name")), "name")),
filterTerm) filterTerm)
) )
) )
.orderBy(order) .orderBy(order)
); );
if (firstResult >= 0) { if (firstResult >= 0) {
query.setFirstResult(firstResult); query.setFirstResult(firstResult);
@ -582,35 +594,35 @@ public class AssetFolderBrowserController {
LOGGER.debug("The database contains {} assets.", LOGGER.debug("The database contains {} assets.",
entityManager.createQuery(criteria.select(fromAsset) entityManager.createQuery(criteria.select(fromAsset)
.where( .where(
builder.and( builder.and(
builder.equal(join.get("category"), builder.equal(join.get("category"),
folder), folder),
builder.equal(join.get("type"), builder.equal(join.get("type"),
CmsConstants.CATEGORIZATION_TYPE_FOLDER), CmsConstants.CATEGORIZATION_TYPE_FOLDER),
builder.like(builder.lower( builder.like(builder.lower(
fromAsset.get( fromAsset.get(
"displayName")), "displayName")),
filterTerm) filterTerm)
))).getResultList().size()); ))).getResultList().size());
final TypedQuery<Asset> query = entityManager final TypedQuery<Asset> query = entityManager
.createQuery( .createQuery(
criteria.select(fromAsset) criteria.select(fromAsset)
.where( .where(
builder.and( builder.and(
builder.equal(join.get( builder.equal(join.get(
"category"), folder), "category"), folder),
builder.equal(join.get("type"), builder.equal(join.get("type"),
CmsConstants.CATEGORIZATION_TYPE_FOLDER), CmsConstants.CATEGORIZATION_TYPE_FOLDER),
builder.like(builder.lower( builder.like(builder.lower(
fromAsset.get( fromAsset.get(
"displayName")), "displayName")),
filterTerm) filterTerm)
) )
) )
.orderBy(order) .orderBy(order)
); );
if (firstResult >= 0) { if (firstResult >= 0) {
query.setFirstResult(firstResult); query.setFirstResult(firstResult);

View File

@ -35,9 +35,10 @@ class AssetFolderBrowserTableModel implements TableModel {
protected static final int COL_NAME = 0; protected static final int COL_NAME = 0;
protected static final int COL_TITLE = 1; protected static final int COL_TITLE = 1;
protected static final int COL_TYPE = 2; protected static final int COL_TYPE = 2;
protected static final int COL_CREATION_DATE = 3; protected static final int COL_THUMBNAIL = 3;
protected static final int COL_LAST_MODIFIED = 4; protected static final int COL_CREATION_DATE = 4;
protected static final int COL_DELETEABLE = 5; protected static final int COL_LAST_MODIFIED = 5;
protected static final int COL_DELETEABLE = 6;
private final Iterator<AssetFolderBrowserTableRow> iterator; private final Iterator<AssetFolderBrowserTableRow> iterator;
private AssetFolderBrowserTableRow currentRow; private AssetFolderBrowserTableRow currentRow;
@ -79,6 +80,8 @@ class AssetFolderBrowserTableModel implements TableModel {
} else { } else {
return new GlobalizedMessage(typeLabelKey, typeLabelBundle); return new GlobalizedMessage(typeLabelKey, typeLabelBundle);
} }
case COL_THUMBNAIL:
return currentRow.getThumbnailUrl();
case COL_CREATION_DATE: case COL_CREATION_DATE:
return currentRow.getCreated(); return currentRow.getCreated();
case COL_LAST_MODIFIED: case COL_LAST_MODIFIED:

View File

@ -30,6 +30,7 @@ class AssetFolderBrowserTableRow {
private String objectUuid; private String objectUuid;
private String name; private String name;
private String title; private String title;
private String thumbnailUrl;
private String typeLabelBundle; private String typeLabelBundle;
private String typeLabelKey; private String typeLabelKey;
private Date created; private Date created;
@ -68,6 +69,14 @@ class AssetFolderBrowserTableRow {
public void setTitle(final String title) { public void setTitle(final String title) {
this.title = title; this.title = title;
} }
public String getThumbnailUrl() {
return thumbnailUrl;
}
public void setThumbnailUrl(final String thumbnailUrl) {
this.thumbnailUrl = thumbnailUrl;
}
public String getTypeLabelBundle() { public String getTypeLabelBundle() {
return typeLabelBundle; return typeLabelBundle;

View File

@ -62,6 +62,76 @@ public class Images {
@Inject @Inject
private AssetRepository assetRepo; private AssetRepository assetRepo;
@GET
@Path("/uuid-{uuid}")
public Response getImageByUuid(
@PathParam("content-section")
final String sectionName,
@PathParam("uuid")
final String uuid,
@QueryParam("width")
@DefaultValue("-1")
final String widthParam,
@QueryParam("height")
@DefaultValue("-1")
final String heightParam) {
final Optional<Asset> asset = assetRepo
.findByUuidAndType(uuid, Image.class);
if (asset.isPresent()) {
if (asset.get() instanceof Image) {
return loadImage((Image) asset.get(), widthParam, heightParam);
} else {
return Response
.status(Response.Status.NOT_FOUND)
.entity(String
.format("The asset with the requested UUID \"%s\" "
+ "is not an image.",
uuid))
.build();
}
} else {
return Response
.status(Response.Status.NOT_FOUND)
.entity(String
.format("The requested image \"%s\" does not exist.",
uuid))
.build();
}
}
@GET
@Path("/uuid-{uuid}/properties")
public Response getImagePropertiesByUuid(
@PathParam("content-section") final String sectionName,
@PathParam("uuid") final String uuid) {
final Optional<Asset> asset = assetRepo.findByUuidAndType(uuid,
Image.class);
if (asset.isPresent()) {
if (asset.get() instanceof Image) {
return readImageProperties((Image) asset.get());
} else {
return Response
.status(Response.Status.NOT_FOUND)
.entity(String
.format("The asset with the requested UUID \"%s\" "
+ "is not an image.",
uuid))
.build();
}
} else {
return Response
.status(Response.Status.NOT_FOUND)
.entity(String
.format("The requested image \"%s\" does not exist.",
uuid))
.build();
}
}
/** /**
* Return the image requested by the provided content section and path. * Return the image requested by the provided content section and path.
* *
@ -119,80 +189,7 @@ public class Images {
if (asset.isPresent()) { if (asset.isPresent()) {
if (asset.get() instanceof Image) { if (asset.get() instanceof Image) {
final Image image = (Image) asset.get(); return loadImage((Image) asset.get(), widthParam, heightParam);
final byte[] data = image.getData();
final String mimeType = image.getMimeType().toString();
final InputStream inputStream = new ByteArrayInputStream(data);
final BufferedImage bufferedImage;
final String imageFormat;
try {
final ImageInputStream imageInputStream = ImageIO
.createImageInputStream(inputStream);
final Iterator<ImageReader> readers = ImageIO
.getImageReaders(imageInputStream);
final ImageReader imageReader;
if (readers.hasNext()) {
imageReader = readers.next();
} else {
LOGGER.error("No image reader for image {} (UUID: {}) "
+ "available.",
image.getDisplayName(),
image.getUuid());
return Response.serverError().build();
}
imageReader.setInput(imageInputStream);
bufferedImage = imageReader.read(0);
imageFormat = imageReader.getFormatName();
} catch (IOException ex) {
LOGGER.error("Failed to load image {} (UUID: {}).",
image.getDisplayName(),
image.getUuid());
LOGGER.error(ex);
return Response.serverError().build();
}
// Yes, this is correct. The parameters provided in the URL
// are expected to be integers. The private scaleImage method
// works with floats to be accurate (divisions are performed
// with the values for width and height)
final int width = parseScaleParameter(widthParam, "width");
final int height = parseScaleParameter(heightParam, "height");
final java.awt.Image scaledImage = scaleImage(bufferedImage,
width,
height);
final ByteArrayOutputStream outputStream
= new ByteArrayOutputStream();
final BufferedImage bufferedScaledImage = new BufferedImage(
scaledImage.getWidth(null),
scaledImage.getHeight(null),
bufferedImage.getType());
bufferedScaledImage
.getGraphics()
.drawImage(scaledImage, 0, 0, null);
try {
ImageIO
.write(bufferedScaledImage, imageFormat, outputStream);
} catch (IOException ex) {
LOGGER.error("Failed to render scaled variant of image {} "
+ "(UUID: {}).",
image.getDisplayName(),
image.getUuid());
LOGGER.error(ex);
return Response.serverError().build();
}
// return Response
// .ok(String.format(
// "Requested image \"%s\" in content section \"%s\"",
// path,
// section.get().getLabel()),
// "text/plain")
// .build();
return Response
.ok(outputStream.toByteArray(), mimeType)
.build();
} else { } else {
return Response return Response
.status(Response.Status.NOT_FOUND) .status(Response.Status.NOT_FOUND)
@ -210,16 +207,6 @@ public class Images {
path)) path))
.build(); .build();
} }
// final Response.ResponseBuilder builder = Response
// .ok(String.format(
// "Requested image \"%s\" from folder \"%s\" in content section \"%s\"",
// imageName,
// folderPath,
// section.get().getLabel()),
// "text/plain");
//
// return builder.build();
} }
/** /**
@ -253,39 +240,7 @@ public class Images {
if (asset.isPresent()) { if (asset.isPresent()) {
if (asset.get() instanceof Image) { if (asset.get() instanceof Image) {
final Image image = (Image) asset.get(); return readImageProperties((Image) asset.get());
final byte[] data = image.getData();
final String mimeType = image.getMimeType().toString();
final InputStream inputStream = new ByteArrayInputStream(data);
final BufferedImage bufferedImage;
try {
bufferedImage = ImageIO.read(inputStream);
} catch (IOException ex) {
LOGGER.error("Failed to load image {} (UUID: {}).",
image.getDisplayName(),
image.getUuid());
LOGGER.error(ex);
return Response.serverError().build();
}
final String imageProperties = String
.format("{%n"
+ " \"name\": \"%s\",%n"
+ " \"filename\": \"%s\",%n"
+ " \"mimetype\": \"%s\",%n"
+ " \"width\": %d,%n"
+ " \"height\": %d%n"
+ "}",
image.getDisplayName(),
image.getFileName(),
mimeType,
bufferedImage.getWidth(),
bufferedImage.getHeight());
return Response
.ok(imageProperties, "application/json")
.build();
} else { } else {
return Response return Response
.status(Response.Status.NOT_FOUND) .status(Response.Status.NOT_FOUND)
@ -305,6 +260,134 @@ public class Images {
} }
} }
/**
* Helper method for loading the image from the {@link Image} asset entity.
*
* This method also does the scaling of the image.
*
* @param image The image asset containing the image.
* @param widthParam The value of the width parameter.
* @param heightParam The value of the height parameter.
*
* @return The {@link Response} for sending the (scaled) image to the
* requesting user agent.
*/
private Response loadImage(final Image image,
final String widthParam,
final String heightParam) {
final byte[] data = image.getData();
final String mimeType = image.getMimeType().toString();
final InputStream inputStream = new ByteArrayInputStream(data);
final BufferedImage bufferedImage;
final String imageFormat;
try {
final ImageInputStream imageInputStream = ImageIO
.createImageInputStream(inputStream);
final Iterator<ImageReader> readers = ImageIO
.getImageReaders(imageInputStream);
final ImageReader imageReader;
if (readers.hasNext()) {
imageReader = readers.next();
} else {
LOGGER.error("No image reader for image {} (UUID: {}) "
+ "available.",
image.getDisplayName(),
image.getUuid());
return Response.serverError().build();
}
imageReader.setInput(imageInputStream);
bufferedImage = imageReader.read(0);
imageFormat = imageReader.getFormatName();
} catch (IOException ex) {
LOGGER.error("Failed to load image {} (UUID: {}).",
image.getDisplayName(),
image.getUuid());
LOGGER.error(ex);
return Response.serverError().build();
}
// Yes, this is correct. The parameters provided in the URL
// are expected to be integers. The private scaleImage method
// works with floats to be accurate (divisions are performed
// with the values for width and height)
final int width = parseScaleParameter(widthParam, "width");
final int height = parseScaleParameter(heightParam, "height");
final java.awt.Image scaledImage = scaleImage(bufferedImage,
width,
height);
final ByteArrayOutputStream outputStream
= new ByteArrayOutputStream();
final BufferedImage bufferedScaledImage = new BufferedImage(
scaledImage.getWidth(null),
scaledImage.getHeight(null),
bufferedImage.getType());
bufferedScaledImage
.getGraphics()
.drawImage(scaledImage, 0, 0, null);
try {
ImageIO
.write(bufferedScaledImage, imageFormat, outputStream);
} catch (IOException ex) {
LOGGER.error("Failed to render scaled variant of image {} "
+ "(UUID: {}).",
image.getDisplayName(),
image.getUuid());
LOGGER.error(ex);
return Response.serverError().build();
}
return Response
.ok(outputStream.toByteArray(), mimeType)
.build();
}
/**
* Helper method for reading the image properties and converting them into
* an JSON response.
*
* @param image The image which properties are read.
*
* @return A {@link Response} with the image properties as JSON.
*/
private Response readImageProperties(final Image image) {
final byte[] data = image.getData();
final String mimeType = image.getMimeType().toString();
final InputStream inputStream = new ByteArrayInputStream(data);
final BufferedImage bufferedImage;
try {
bufferedImage = ImageIO.read(inputStream);
} catch (IOException ex) {
LOGGER.error("Failed to load image {} (UUID: {}).",
image.getDisplayName(),
image.getUuid());
LOGGER.error(ex);
return Response.serverError().build();
}
final String imageProperties = String
.format("{%n"
+ " \"name\": \"%s\",%n"
+ " \"filename\": \"%s\",%n"
+ " \"mimetype\": \"%s\",%n"
+ " \"width\": %d,%n"
+ " \"height\": %d%n"
+ "}",
image.getDisplayName(),
image.getFileName(),
mimeType,
bufferedImage.getWidth(),
bufferedImage.getHeight());
return Response
.ok(imageProperties, "application/json")
.build();
}
/** /**
* Helper method for parsing the parameters for scaling an image into * Helper method for parsing the parameters for scaling an image into
* integers. * integers.

View File

@ -386,3 +386,4 @@ related_info_step_description=Add related information
cms.ui.authoring.file_upload.auto_detect=(Auto-Detect) cms.ui.authoring.file_upload.auto_detect=(Auto-Detect)
cms.ui.upload_new_content=Upload new content cms.ui.upload_new_content=Upload new content
cms.ui.asset.name=Name cms.ui.asset.name=Name
cms.ui.asset.thumbnail=Preview

View File

@ -383,3 +383,4 @@ related_info_step_description=Weiterf\u00fchrende Informationen hinzuf\u00fcgen
cms.ui.authoring.file_upload.auto_detect=(Automatisch erkennen) cms.ui.authoring.file_upload.auto_detect=(Automatisch erkennen)
cms.ui.upload_new_content=Neuen Inhalt hochladen cms.ui.upload_new_content=Neuen Inhalt hochladen
cms.ui.asset.name=Name cms.ui.asset.name=Name
cms.ui.asset.thumbnail=Vorschau

View File

@ -342,3 +342,4 @@ related_info_step_description=Add related information
cms.ui.authoring.file_upload.auto_detect=(Auto-Detect) cms.ui.authoring.file_upload.auto_detect=(Auto-Detect)
cms.ui.upload_new_content=Upload new content cms.ui.upload_new_content=Upload new content
cms.ui.asset.name=Name cms.ui.asset.name=Name
cms.ui.asset.thumbnail=Preview

View File

@ -52,9 +52,12 @@ import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.PageContext; import javax.servlet.jsp.PageContext;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import java.net.URLEncoder; import java.net.URLEncoder;
import javax.servlet.ServletRequestWrapper;
/** /**
* Class static helper methods for request dispatching. Contains various * Class static helper methods for request dispatching. Contains various
* generally useful procedural abstractions. * generally useful procedural abstractions.
@ -70,7 +73,8 @@ public final class DispatcherHelper implements DispatcherConstants {
* set com.arsdigita.dispatcher.DispatcherHelper=DEBUG by uncommenting or * set com.arsdigita.dispatcher.DispatcherHelper=DEBUG by uncommenting or
* adding the line. * adding the line.
*/ */
private static final Logger LOGGER = LogManager.getLogger(DispatcherHelper.class); private static final Logger LOGGER = LogManager.getLogger(
DispatcherHelper.class);
private static String s_webappCtx; private static String s_webappCtx;
private static String s_staticURL; private static String s_staticURL;
private static boolean s_cachingActive; private static boolean s_cachingActive;
@ -177,7 +181,14 @@ public final class DispatcherHelper implements DispatcherConstants {
} }
public static String getDispatcherPrefix(HttpServletRequest req) { public static String getDispatcherPrefix(HttpServletRequest req) {
return (String) req.getAttribute(DISPATCHER_PREFIX_ATTR); final HttpServletRequest request;
if (req instanceof ShiroHttpServletRequest) {
request = (HttpServletRequest) ((ServletRequestWrapper) req)
.getRequest();
} else {
request = req;
}
return (String) request.getAttribute(DISPATCHER_PREFIX_ATTR);
} }
public static void setDispatcherPrefix(HttpServletRequest req, public static void setDispatcherPrefix(HttpServletRequest req,
@ -541,10 +552,10 @@ public final class DispatcherHelper implements DispatcherConstants {
if (previous instanceof MultipartHttpServletRequest) { if (previous instanceof MultipartHttpServletRequest) {
LOGGER.debug("Build new multipart request from previous " LOGGER.debug("Build new multipart request from previous "
+ previous + " and current " + orig); + previous + " and current " + orig);
MultipartHttpServletRequest previousmp MultipartHttpServletRequest previousmp
= (MultipartHttpServletRequest) previous; = (MultipartHttpServletRequest) previous;
sreq = new MultipartHttpServletRequest(previousmp, sreq = new MultipartHttpServletRequest(previousmp,
orig); orig);
@ -567,7 +578,7 @@ public final class DispatcherHelper implements DispatcherConstants {
} }
} else { } else {
LOGGER.debug("The request is not multipart; proceeding " LOGGER.debug("The request is not multipart; proceeding "
+ "without wrapping the request"); + "without wrapping the request");
} }
return sreq; return sreq;
} }
@ -681,14 +692,14 @@ public final class DispatcherHelper implements DispatcherConstants {
destination = URL.there(req, url.substring(0, sep), params); destination = URL.there(req, url.substring(0, sep), params);
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Setting destination with map to " LOGGER.debug("Setting destination with map to "
+ destination); + destination);
} }
} }
throw new RedirectSignal(destination, true); throw new RedirectSignal(destination, true);
} else { } else {
if (LOGGER.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Redirecting to URL without using URL.there. " LOGGER.debug("Redirecting to URL without using URL.there. "
+ "URL is " + url); + "URL is " + url);
} }
throw new RedirectSignal(url, true); throw new RedirectSignal(url, true);
} }
@ -875,7 +886,7 @@ public final class DispatcherHelper implements DispatcherConstants {
LOGGER.warn( LOGGER.warn(
"webappContext changed. Expected='" + s_webappCtx "webappContext changed. Expected='" + s_webappCtx
+ "' found='" + webappCtx + "' found='" + webappCtx
+ "'.\nPerhaps the enterprise.init " + "'.\nPerhaps the enterprise.init "
+ "com.arsdigita.dispatcher.Initializer webappContext " + "com.arsdigita.dispatcher.Initializer webappContext "
+ "parameter is wrong."); + "parameter is wrong.");
// Save the webappCtx from the request for future use. // Save the webappCtx from the request for future use.
@ -1193,7 +1204,7 @@ public final class DispatcherHelper implements DispatcherConstants {
// Set the preferedLocale to the default locale (first entry in the // Set the preferedLocale to the default locale (first entry in the
// config parameter list) // config parameter list)
Locale preferedLocale Locale preferedLocale
= new Locale(kernelConfig.getDefaultLanguage(), "", ""); = new Locale(kernelConfig.getDefaultLanguage(), "", "");
// The ACCEPTED_LANGUAGES from the client // The ACCEPTED_LANGUAGES from the client
Enumeration locales = null; Enumeration locales = null;

View File

@ -706,7 +706,7 @@ public class URL {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Bean<ConfigurationManager> bean final Bean<ConfigurationManager> bean
= (Bean<ConfigurationManager>) iterator = (Bean<ConfigurationManager>) iterator
.next(); .next();
final CreationalContext<ConfigurationManager> ctx = beanManager final CreationalContext<ConfigurationManager> ctx = beanManager
.createCreationalContext(bean); .createCreationalContext(bean);
confManager = (ConfigurationManager) beanManager.getReference( confManager = (ConfigurationManager) beanManager.getReference(