View Javadoc
1   /**
2    * This file Copyright (c) 2003-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.ui.imageprovider;
35  
36  import info.magnolia.cms.beans.runtime.FileProperties;
37  import info.magnolia.context.MgnlContext;
38  import info.magnolia.jcr.util.NodeTypes.LastModified;
39  import info.magnolia.jcr.util.NodeUtil;
40  import info.magnolia.link.LinkUtil;
41  import info.magnolia.ui.imageprovider.definition.ImageProviderDefinition;
42  import info.magnolia.ui.vaadin.integration.contentconnector.ContentConnector;
43  import info.magnolia.ui.vaadin.integration.jcr.JcrNodeAdapter;
44  
45  import java.io.InputStream;
46  import java.util.Calendar;
47  import java.util.Set;
48  
49  import javax.inject.Inject;
50  import javax.jcr.Node;
51  import javax.jcr.RepositoryException;
52  
53  import org.apache.commons.lang3.StringUtils;
54  import org.apache.jackrabbit.JcrConstants;
55  import org.apache.jackrabbit.util.Text;
56  import org.slf4j.Logger;
57  import org.slf4j.LoggerFactory;
58  
59  import com.google.common.collect.ImmutableSet;
60  import com.google.common.net.MediaType;
61  import com.vaadin.server.ExternalResource;
62  import com.vaadin.server.StreamResource;
63  import com.vaadin.server.StreamResource.StreamSource;
64  import com.vaadin.v7.data.Item;
65  
66  /**
67   * This implementation of {@link ImageProvider} provides portrait or thumbnail images for JCR-based content.
68   * <p>
69   * It expects a given Node to have a binary sub-node named according to {@link ImageProviderDefinition#getOriginalImageNodeName()},
70   * and will only resolve images when the binary is of mime-type image/*.
71   * It also relies on imaging module to generate and store the preview images.
72   *
73   * @see ImageProviderDefinition
74   * @deprecated since 6.0. Use new framework and {@link info.magnolia.ui.contentapp.preview.JcrPreviewProvider}.
75   */
76  @Deprecated
77  public class DefaultImageProvider extends AbstractImageProvider {
78  
79      /**
80       * @deprecated since 5.4.3, not used anymore; default icon for unknown type is provided by {@link AbstractImageProvider#resolveIconClassName(String)}.
81       */
82      @Deprecated
83      public final String ICON_CLASS_DEFAULT = "file";
84  
85      private static final Set<String> UNSUPPORTED_IMAGE_TYPES = ImmutableSet.of(
86              "image/x-icon",
87              "image/x-portable-bitmap",
88              "image/x-xpixmap",
89              "image/x-portable-graymap",
90              "image/x-cmu-raster",
91              "image/x-portable-pixmap",
92              "image/x-xbitmap",
93              "image/x-rgb",
94              "image/x-xwindowdump",
95              "image/x-cmx",
96              "image/cis-cod",
97              "image/ief"
98      );
99  
100     private static final Logger log = LoggerFactory.getLogger(DefaultImageProvider.class);
101 
102     private final ImageProviderDefinition definition;
103 
104     @Inject
105     public DefaultImageProvider(ImageProviderDefinition definition, ContentConnector contentConnector) {
106         super(contentConnector);
107         this.definition = definition;
108     }
109 
110     @Override
111     protected String resolveImagePath(Item item, String generator) {
112         String imagePath = null;
113 
114         if (item instanceof JcrNodeAdapter) {
115             JcrNodeAdapter jcrAdapter = (JcrNodeAdapter) item;
116             Node node = jcrAdapter.getJcrItem();
117             String workspace = jcrAdapter.getWorkspace();
118 
119             // TODO when deprecated method is removed, inline #getGeneratorImagePath logic here
120             imagePath = getGeneratorImagePath(workspace, node, generator);
121         } else {
122             log.debug("DefaultImageProvider works with info.magnolia.ui.vaadin.integration.jcr.JcrNodeAdapter only.");
123         }
124 
125         return imagePath;
126     }
127 
128     /**
129      * Resolves the image path from a JCR Node. This is kept for backwards compatibility.
130      *
131      * @deprecated Since 5.4.3, implement {@link #resolveImagePath(Item, String)} instead. Also consider extending {@link AbstractImageProvider}.
132      */
133     @Deprecated
134     protected String getGeneratorImagePath(String workspace, Node node, String generator) {
135         String imagePath = null;
136         if (node != null) {
137             try {
138                 Node imageNode = node.getNode(definition.getOriginalImageNodeName());
139 
140                 String imageName;
141                 if (imageNode.hasProperty(FileProperties.PROPERTY_FILENAME)) {
142                     imageName = imageNode.getProperty(FileProperties.PROPERTY_FILENAME).getString();
143                 } else {
144                     imageName = node.getName();
145                 }
146 
147                 // Crafting link manually;
148                 // ImagingSupport should eventually create the link but it only supports the original variation so far
149                 //  manual:  /.imaging/thumbnail/dam/374f8dbe-a8d7-4829-b9e7-3386a5ecb58f/blindtiger.png.jpg
150                 // imaging:  /.imaging/default/dam/blindtiger.png/jcr:content.png
151                 imagePath = Text.escapePath(MgnlContext.getContextPath() + "/" + definition.getImagingServletPath() + "/" + generator + "/" + workspace + "/" + imageNode.getIdentifier() + "/" + imageName + "." + definition.getImageExtension());
152 
153                 // Add cache fingerprint so that browser caches asset only until asset is modified.
154                 Calendar lastModified = LastModified.getLastModified(node);
155                 imagePath = LinkUtil.addFingerprintToLink(imagePath, lastModified);
156 
157             } catch (RepositoryException e) {
158                 log.warn("Could not get name or identifier from imageNode: {}", e.getMessage());
159             }
160         }
161         return imagePath;
162     }
163 
164     @Override
165     protected Object resolveImageResource(Item item, String generator) {
166         Object resource = null;
167 
168         if (item instanceof JcrNodeAdapter) {
169             JcrNodeAdapter jcrAdapter = (JcrNodeAdapter) item;
170             Node node = jcrAdapter.getJcrItem();
171 
172             try {
173                 final Node imageNode = getImageNode(node);
174                 if (imageNode == null) {
175                     return null;
176                 }
177                 String mimeType = imageNode.getProperty(FileProperties.PROPERTY_CONTENTTYPE).getString();
178 
179                 if (isImage(mimeType)) {
180                     if (MediaType.SVG_UTF_8.is(MediaType.parse(mimeType)) || MediaType.ICO.is(MediaType.parse(mimeType))) {
181                         ImageStreamSource iss = new ImageStreamSource(imageNode.getIdentifier(), imageNode.getSession().getWorkspace().getName());
182                         // By default a StreamResource is cached for one year - filename contains the last modified date so that image is cached by the browser until changed.
183                         String filename = imageNode.getIdentifier() + LastModified.getLastModified(imageNode).getTimeInMillis();
184                         StreamResource streamResource = new StreamResource(iss, filename);
185                         streamResource.setMIMEType(mimeType);
186                         resource = streamResource;
187                     } else if (!UNSUPPORTED_IMAGE_TYPES.contains(mimeType)) {
188                         String path = resolveImagePath(item, generator);
189                         if (StringUtils.isNotBlank(path)) {
190                             resource = new ExternalResource(path, MediaType.PNG.toString());
191                         }
192                     }
193                 } else {
194                     resource = resolveIconClassName(mimeType);
195                 }
196             } catch (RepositoryException e) {
197                 log.debug("Could not get name or identifier from imageNode: {}", e.getMessage());
198             }
199         } else {
200             log.debug("DefaultImageProvider works with info.magnolia.ui.vaadin.integration.jcr.JcrNodeAdapter only.");
201         }
202 
203         return resource;
204     }
205 
206     protected Node getImageNode(Node node) throws RepositoryException {
207         return node.getNode(definition.getOriginalImageNodeName());
208     }
209 
210     private static class ImageStreamSource implements StreamSource {
211         private final String id;
212         private final String workspace;
213 
214         public ImageStreamSource(String id, String workspace) {
215             this.id = id;
216             this.workspace = workspace;
217         }
218 
219         @Override
220         public InputStream getStream() {
221             try {
222                 Node node = NodeUtil.getNodeByIdentifier(workspace, id);
223                 if (node != null && node.hasProperty(JcrConstants.JCR_DATA)) {
224                     return node.getProperty(JcrConstants.JCR_DATA).getBinary().getStream();
225                 }
226             } catch (RepositoryException e) {
227                 e.printStackTrace();
228             }
229             return null;
230         }
231     }
232 }