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

Former-commit-id: f7d7293868
pull/2/head
jensp 2017-07-27 10:26:14 +00:00
parent 01954befb0
commit 776e1719d3
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.*;
@ -484,6 +488,14 @@ public class AssetFolderBrowserController {
} 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());

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;
@ -69,6 +70,14 @@ class AssetFolderBrowserTableRow {
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,7 +189,93 @@ 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);
} else {
return Response
.status(Response.Status.NOT_FOUND)
.entity(String
.format("The asset found at the requested path \"%s\" "
+ "is not an image.",
path))
.build();
}
} else {
return Response
.status(Response.Status.NOT_FOUND)
.entity(String
.format("The requested image \"%s\" does not exist.",
path))
.build();
}
}
/**
* Provides several properties of an image to a user agent as JSON.
*
* @param sectionName The name of the content section which contains the
* image.
* @param path The path to the image.
*
* @return A {@link Response} with the informations about the requested
* image.
*/
@GET
@Path("/{path:.*}/properties")
public Response getImageProperties(
@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<Asset> asset = assetRepo.findByPath(section.get(),
path);
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 found at the requested path \"%s\" "
+ "is not an image.",
path))
.build();
}
} else {
return Response
.status(Response.Status.NOT_FOUND)
.entity(String
.format("The requested image \"%s\" does not exist.",
path))
.build();
}
}
/**
* 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 byte[] data = image.getData();
final String mimeType = image.getMimeType().toString(); final String mimeType = image.getMimeType().toString();
@ -183,77 +339,21 @@ public class Images {
return Response.serverError().build(); 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 return Response
.ok(outputStream.toByteArray(), mimeType) .ok(outputStream.toByteArray(), mimeType)
.build(); .build();
} else {
return Response
.status(Response.Status.NOT_FOUND)
.entity(String
.format("The asset found at the requested path \"%s\" "
+ "is not an image.",
path))
.build();
}
} else {
return Response
.status(Response.Status.NOT_FOUND)
.entity(String
.format("The requested image \"%s\" does not exist.",
path))
.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();
} }
/** /**
* Provides several properties of an image to a user agent as JSON. * Helper method for reading the image properties and converting them into
* an JSON response.
* *
* @param sectionName The name of the content section which contains the * @param image The image which properties are read.
* image.
* @param path The path to the image.
* *
* @return A {@link Response} with the informations about the requested * @return A {@link Response} with the image properties as JSON.
* image.
*/ */
@GET private Response readImageProperties(final Image image) {
@Path("/{path:.*}/properties")
public Response getImageProperties(
@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<Asset> asset = assetRepo.findByPath(section.get(),
path);
if (asset.isPresent()) {
if (asset.get() instanceof Image) {
final Image image = (Image) asset.get();
final byte[] data = image.getData(); final byte[] data = image.getData();
final String mimeType = image.getMimeType().toString(); final String mimeType = image.getMimeType().toString();
@ -286,23 +386,6 @@ public class Images {
return Response return Response
.ok(imageProperties, "application/json") .ok(imageProperties, "application/json")
.build(); .build();
} else {
return Response
.status(Response.Status.NOT_FOUND)
.entity(String
.format("The asset found at the requested path \"%s\" "
+ "is not an image.",
path))
.build();
}
} else {
return Response
.status(Response.Status.NOT_FOUND)
.entity(String
.format("The requested image \"%s\" does not exist.",
path))
.build();
}
} }
/** /**

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,