View Javadoc
1   /**
2    * This file Copyright (c) 2012-2016 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.templating.jsp.taglib;
35  
36  import info.magnolia.cms.beans.runtime.FileProperties;
37  import info.magnolia.cms.core.Content;
38  import info.magnolia.cms.core.NodeData;
39  import info.magnolia.context.MgnlContext;
40  import info.magnolia.jcr.util.NodeTypes;
41  
42  import java.awt.Color;
43  import java.io.File;
44  import java.io.FileInputStream;
45  import java.io.FileNotFoundException;
46  import java.io.IOException;
47  import java.io.InputStream;
48  import java.text.CharacterIterator;
49  import java.text.StringCharacterIterator;
50  import java.util.Calendar;
51  import java.util.GregorianCalendar;
52  import java.util.TimeZone;
53  
54  import javax.jcr.AccessDeniedException;
55  import javax.jcr.Node;
56  import javax.jcr.PathNotFoundException;
57  import javax.jcr.PropertyType;
58  import javax.jcr.RepositoryException;
59  import javax.jcr.Session;
60  import javax.servlet.http.HttpServletRequest;
61  import javax.servlet.jsp.PageContext;
62  import javax.servlet.jsp.tagext.SimpleTagSupport;
63  
64  import org.apache.commons.io.IOUtils;
65  import org.apache.commons.lang3.StringUtils;
66  import org.devlib.schmidt.imageinfo.ImageInfo;
67  import org.slf4j.Logger;
68  import org.slf4j.LoggerFactory;
69  
70  
71  /**
72   * A base class for image related tags e.g. {@link ScaleImageTag}.
73   */
74  public abstract class BaseImageTag extends SimpleTagSupport {
75  
76      /**
77       * The value of the extension nodeData in the properties node.
78       */
79      protected static final String PROPERTIES_EXTENSION_VALUE = "PNG";
80  
81      /**
82       * The valye of the contentType nodeData in the properties node.
83       */
84      protected static final String PROPERTIES_CONTENTTYPE_VALUE = "image/png";
85  
86      private static Logger log = LoggerFactory.getLogger(BaseImageTag.class);
87  
88      /**
89       * Attribute: The node where the images are to be saved. If null, the default will be the current active page.
90       */
91      protected String parentContentNodeName;
92  
93      /**
94       * Attribute: The name of the new content node to create. The images will be saved under this node. If this name
95       * starts with a '/', it will be assumed to be a node handle that is relative to the rooot of the website.
96       * Otherwise, it is assumed to be a path relative to the currentActivePage.
97       */
98      protected String imageContentNodeName;
99  
100     /**
101      * Setter for the <code>imageContentNodeName</code> tag attribute.
102      */
103     public void setImageContentNodeName(String imageContentNodeName) {
104         this.imageContentNodeName = imageContentNodeName;
105     }
106 
107     /**
108      * Setter for the <code>parentContentNodeName</code> tag attribute.
109      */
110     public void setParentContentNodeName(String parentContentNodeName) {
111         this.parentContentNodeName = parentContentNodeName;
112     }
113 
114     protected abstract String getFilename();
115 
116     protected HttpServletRequest getRequest() {
117         return (HttpServletRequest) ((PageContext) this.getJspContext()).getRequest();
118     }
119 
120     protected Node getImageContentNode() throws PathNotFoundException, RepositoryException,
121         info.magnolia.cms.security.AccessDeniedException {
122         Session session = MgnlContext.getJCRSession(MgnlContext.getAggregationState().getRepository());
123 
124         Node imageContentNode;
125         Node currentActivePage = MgnlContext.getJCRSession(MgnlContext.getAggregationState().getRepository()).getNode(MgnlContext.getAggregationState().getHandle());
126 
127         Node paragraph = MgnlContext.getAggregationState().getCurrentContentNode();
128 
129         Node parentContentNode = null;
130         // set the image parent node
131         if (StringUtils.isEmpty(this.parentContentNodeName)) {
132             parentContentNode = paragraph != null ? paragraph : currentActivePage;
133         }
134         else {
135             // if this name starts with a '/', then assume it is a node handle
136             // otherwise assume that its is a path relative to the current active page
137             if (this.parentContentNodeName.startsWith("/")) {
138                 parentContentNode = session.getNode(this.parentContentNodeName);
139             }
140             else {
141                 parentContentNode = session.getNode(currentActivePage.getPath() + "/" + this.parentContentNodeName);
142             }
143         }
144         // set the node under which the images will be saved
145         imageContentNode = null;
146         if (StringUtils.isEmpty(this.imageContentNodeName)) {
147             imageContentNode = parentContentNode;
148         }
149         else if (parentContentNode.hasNode(this.imageContentNodeName)) {
150             imageContentNode = parentContentNode.getNode(this.imageContentNodeName);
151         }
152         else {
153             imageContentNode = parentContentNode.addNode(this.imageContentNodeName, NodeTypes.ContentNode.NAME);
154             session.save();
155         }
156         return imageContentNode;
157     }
158 
159     /**
160      * Replace any special characters that are not letters or numbers with a replacement string. The two exceptions are
161      * '-' and '_', which are allowed.
162      */
163     public String convertToSimpleString(String string) {
164 
165         final StringBuffer result = new StringBuffer();
166 
167         final StringCharacterIterator iterator = new StringCharacterIterator(string);
168         char character = iterator.current();
169         while (character != CharacterIterator.DONE) {
170             int charType = Character.getType(character);
171             if (charType == Character.SPACE_SEPARATOR) {
172                 result.append("-");
173             }
174             else if ((charType != Character.UPPERCASE_LETTER)
175                 && (charType != Character.LOWERCASE_LETTER)
176                 && (charType != Character.DECIMAL_DIGIT_NUMBER)
177                 && (charType != Character.CONNECTOR_PUNCTUATION)
178                 && (charType != Character.DASH_PUNCTUATION)) {
179                 result.append("u" + (int) character);
180 
181             }
182             else {
183                 // the char is not a special one
184                 // add it to the result as is
185                 result.append(character);
186             }
187             character = iterator.next();
188         }
189         return result.toString();
190     }
191 
192     /**
193      * Converts HEX color to RGB color.
194      * @param hex HEX value
195      */
196     public int[] convertHexToRGB(String hex) {
197         hex.trim();
198         if (hex.startsWith("#")) {
199             hex = hex.substring(1);
200         }
201         if (hex.length() == 3) {
202             // allow three digit codes like for css
203             hex = String.valueOf(hex.charAt(0))
204                 + String.valueOf(hex.charAt(0))
205                 + String.valueOf(hex.charAt(1))
206                 + String.valueOf(hex.charAt(1))
207                 + String.valueOf(hex.charAt(2))
208                 + String.valueOf(hex.charAt(2));
209         }
210 
211         int[] rgb = new int[3];
212         try {
213             // Convert rrggbb string to hex ints
214             rgb[0] = Integer.parseInt(hex.substring(0, 2), 16);
215             rgb[1] = Integer.parseInt(hex.substring(2, 4), 16);
216             rgb[2] = Integer.parseInt(hex.substring(4), 16);
217         }
218         catch (NumberFormatException e) {
219             log.error("NumberFormatException occured during text-to-image conversion: Attempting to convert Hex [{}] color to RGB color: {}", hex, e.getMessage(), e);
220             rgb = new int[]{255, 0, 0}; // red
221         }
222         return rgb;
223     }
224 
225     public Color convertHexToColor(String hex) {
226         int[] rgb = convertHexToRGB(hex);
227         Color color = new Color(rgb[0], rgb[1], rgb[2]);
228         return color;
229     }
230 
231     /**
232      * Create a new imageNode with the image in it. The node is saved under a node that groups all image nodes, whose
233      * name is set to the value of the attribute imageContentNodeName. The name of the node will be set to a name that
234      * is unique for the image. The property that stores the image will be set to the value of
235      * PROPERTIES_FILENAME_VALUE. A sub-node is also created that stores the image properties.
236      * @param imageFile the file
237      * @param imageNode The node that will contain the text images.
238      */
239     protected void createImageNode(File imageFile, Content imageNode) throws PathNotFoundException,
240         AccessDeniedException, RepositoryException, FileNotFoundException, IOException {
241 
242         // Create and save the image data
243         NodeData data;
244         data = imageNode.getNodeData(getFilename());
245 
246         if (!data.isExist()) {
247             data = imageNode.createNodeData(getFilename(), PropertyType.BINARY);
248         }
249 
250         InputStream iis = new FileInputStream(imageFile);
251         data.setValue(iis);
252         IOUtils.closeQuietly(iis);
253 
254         data.setAttribute(FileProperties.PROPERTY_FILENAME, getFilename());
255 
256         data.setAttribute(FileProperties.PROPERTY_CONTENTTYPE, PROPERTIES_CONTENTTYPE_VALUE);
257 
258         Calendar value = new GregorianCalendar(TimeZone.getDefault());
259         data.setAttribute(FileProperties.PROPERTY_LASTMODIFIED, value);
260 
261         data.setAttribute(FileProperties.PROPERTY_SIZE, Long.toString(imageFile.length()));
262 
263         data.setAttribute(FileProperties.PROPERTY_EXTENSION, PROPERTIES_EXTENSION_VALUE);
264 
265         InputStream raf = null;
266         try {
267             ImageInfo ii = new ImageInfo();
268             raf = new FileInputStream(imageFile);
269             ii.setInput(raf);
270             if (ii.check()) {
271                 data.setAttribute(FileProperties.PROPERTY_WIDTH, Long.toString(ii.getWidth()));
272                 data.setAttribute(FileProperties.PROPERTY_HEIGHT, Long.toString(ii.getHeight()));
273 
274             }
275         }
276         catch (FileNotFoundException e) {
277             log.error("FileNotFoundException caught when parsing {}, image data will not be available", imageFile
278                 .getAbsolutePath());
279         }
280         finally {
281             IOUtils.closeQuietly(raf);
282         }
283 
284         // delete the temporary file
285         imageFile.delete();
286 
287         // save the new image node
288         imageNode.getParent().save();
289     }
290 }