1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.contentapp.preview;
35
36 import info.magnolia.cms.beans.runtime.File;
37 import info.magnolia.jcr.RuntimeRepositoryException;
38 import info.magnolia.jcr.util.NodeTypes.LastModified;
39 import info.magnolia.jcr.util.PropertyUtil;
40 import info.magnolia.link.LinkUtil;
41 import info.magnolia.ui.contentapp.browser.preview.PreviewProvider;
42 import info.magnolia.ui.datasource.jcr.JcrDatasourceDefinition;
43
44 import java.io.InputStream;
45 import java.util.Calendar;
46 import java.util.Optional;
47 import java.util.Set;
48
49 import javax.inject.Inject;
50 import javax.inject.Provider;
51 import javax.jcr.Item;
52 import javax.jcr.Node;
53 import javax.jcr.Property;
54 import javax.jcr.RepositoryException;
55 import javax.servlet.http.HttpServletRequest;
56
57 import org.apache.jackrabbit.JcrConstants;
58 import org.apache.jackrabbit.util.Text;
59
60 import com.google.common.collect.ImmutableSet;
61 import com.google.common.net.MediaType;
62 import com.machinezoo.noexception.Exceptions;
63 import com.vaadin.server.ExternalResource;
64 import com.vaadin.server.Resource;
65 import com.vaadin.server.StreamResource;
66 import com.vaadin.server.StreamResource.StreamSource;
67
68 import lombok.Getter;
69 import lombok.Setter;
70
71
72
73
74
75
76
77
78 @Setter
79 @Getter
80 public class JcrPreviewProvider implements PreviewProvider<Item> {
81
82 private static final Set<String> UNSUPPORTED_IMAGE_TYPES = ImmutableSet.of(
83 "image/x-icon",
84 "image/x-portable-bitmap",
85 "image/x-xpixmap",
86 "image/x-portable-graymap",
87 "image/x-cmu-raster",
88 "image/x-portable-pixmap",
89 "image/x-xbitmap",
90 "image/x-rgb",
91 "image/x-xwindowdump",
92 "image/x-cmx",
93 "image/cis-cod",
94 "image/ief"
95 );
96
97 private static final Set<String> SUPPORTED_AUDIO_TYPES = ImmutableSet.of(
98 MediaType.MPEG_AUDIO.toString(),
99 MediaType.WEBM_AUDIO.toString(),
100 MediaType.OGG_AUDIO.toString(),
101 MediaType.MP4_AUDIO.toString(),
102 MediaType.AAC_AUDIO.toString(),
103 "audio/aacp",
104 "audio/flac",
105 "audio/wav",
106 "audio/x-wav",
107 "audio/mp3"
108 );
109
110 private static final Set<String> SUPPORTED_VIDEO_TYPES = ImmutableSet.of(
111 MediaType.MPEG_VIDEO.toString(),
112 MediaType.WEBM_VIDEO.toString(),
113 MediaType.MP4_VIDEO.toString(),
114 MediaType.OGG_VIDEO.toString()
115 );
116
117 private final JcrPreviewDefinition previewDefinition;
118 private final String contextPath;
119 private final LinkProvider linkProvider;
120
121 @Inject
122 JcrPreviewProvider(JcrDatasourceDefinition datasourceDefinition, Provider<HttpServletRequest> requestProvider, LinkProvider linkProvider) {
123 this.contextPath = requestProvider.get().getContextPath();
124 this.previewDefinition = datasourceDefinition.getPreviewDefinition();
125 this.linkProvider = linkProvider;
126 }
127
128 @Override
129 public Optional<Resource> getResource(Item item) {
130 return Optional.ofNullable(previewDefinition)
131 .filter(any -> item.isNode())
132 .map(JcrPreviewDefinition::getNodeName)
133 .map(Exceptions.wrap().function(nodeName -> {
134 final Node node = (Node) item;
135 if (node.hasNode(nodeName)) {
136 Node resourceNode = node.getNode(nodeName);
137 String mimeType = getMimeType(resourceNode);
138 if (mimeType != null) {
139 if (mimeType.matches("image.*")) {
140 return imageResource(mimeType, node, resourceNode);
141 } else if (SUPPORTED_AUDIO_TYPES.contains(mimeType) || SUPPORTED_VIDEO_TYPES.contains(mimeType)) {
142 return new ExternalResource(linkProvider.createLink(node), mimeType);
143 }
144 }
145 }
146 return null;
147 })
148 );
149 }
150
151 private Resource imageResource(String mimeType, Node item, Node imageNode) throws RepositoryException {
152 if (MediaType.SVG_UTF_8.is(MediaType.parse(mimeType)) || MediaType.ICO.is(MediaType.parse(mimeType))) {
153 final String identifier = imageNode.getIdentifier();
154 ImageStreamSource iss = new ImageStreamSource(imageNode);
155
156 String filename = identifier + getLastModified(imageNode).getTimeInMillis();
157 StreamResource streamResource = new StreamResource(iss, filename);
158 streamResource.setMIMEType(mimeType);
159 return streamResource;
160 } else if (!UNSUPPORTED_IMAGE_TYPES.contains(mimeType)) {
161 return resolveImagePath(item, imageNode, previewDefinition.getGenerator()).map(path -> new ExternalResource(path, MediaType.PNG.toString())).orElse(null);
162 }
163 return null;
164 }
165
166 private Optional<String> resolveImagePath(Node node, Node imageNode, String generator) throws RepositoryException {
167 String imageName = PropertyUtil.getString(imageNode, File.PROPERTY_FILENAME);
168 if (imageName == null) {
169 imageName = node.getName();
170 }
171
172
173
174
175 String imagePath = Text.escapePath(contextPath + "/" + previewDefinition.getServletPath() + "/" + generator + "/" + node.getSession().getWorkspace().getName() + "/" + imageNode.getIdentifier() + "/" + imageName + "." + previewDefinition.getExtension());
176
177 Calendar lastModified = getLastModified(node);
178 imagePath = LinkUtil.addFingerprintToLink(imagePath, lastModified);
179 return Optional.ofNullable(imagePath);
180 }
181
182 private Calendar getLastModified(Node node) {
183 try {
184 return LastModified.getLastModified(node);
185 } catch (RepositoryException e) {
186 throw new RuntimeRepositoryException(e);
187 }
188 }
189
190 private String getMimeType(Node imageNode) {
191 return PropertyUtil.getString(imageNode, JcrConstants.JCR_MIMETYPE);
192 }
193
194 private static class ImageStreamSource implements StreamSource {
195 private final Node node;
196
197 ImageStreamSource(Node imageNode) {
198 this.node = imageNode;
199 }
200
201 @Override
202 public InputStream getStream() {
203 final Property property = PropertyUtil.getPropertyOrNull(node, JcrConstants.JCR_DATA);
204 try {
205 return property == null ? null : property.getBinary().getStream();
206 } catch (RepositoryException e) {
207 throw new RuntimeRepositoryException(e);
208 }
209 }
210 }
211
212
213
214
215 public static class LinkProvider {
216
217 @Inject
218 LinkProvider() {}
219
220 String createLink(Node node) {
221 return LinkUtil.createLink(node);
222 }
223 }
224 }