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.cms.beans.config;
35  
36  import info.magnolia.context.MgnlContext;
37  import info.magnolia.jcr.util.NodeTypes;
38  import info.magnolia.jcr.util.NodeUtil;
39  import info.magnolia.jcr.util.PropertyUtil;
40  import info.magnolia.observation.WorkspaceEventListenerRegistration;
41  import info.magnolia.repository.RepositoryConstants;
42  
43  import java.util.Hashtable;
44  import java.util.Iterator;
45  import java.util.Map;
46  
47  import javax.jcr.Node;
48  import javax.jcr.PathNotFoundException;
49  import javax.jcr.RepositoryException;
50  import javax.jcr.Session;
51  
52  import org.apache.commons.lang3.StringUtils;
53  import org.slf4j.Logger;
54  import org.slf4j.LoggerFactory;
55  
56  /**
57   * Manages mappings of file extensions with their MIME types and icon.
58   *
59   * @deprecated since 5.4.6. This class will be replaced (see MAGNOLIA-6576).
60   */
61  @Deprecated
62  public class MIMEMapping {
63      private static final Logger log = LoggerFactory.getLogger(MIMEMapping.class);
64  
65      public static final String ICONS_PATH = "/.resources/file-icons/";
66      public static final String DEFAULT_ICON = ICONS_PATH + "general.png";
67      private static final String NODEPATH = "/server/MIMEMapping";
68      public static final String DEFAULT_ICON_STYLE = "icon-file";
69  
70      private static Map<String, MIMEMappingItem> cachedContent = new Hashtable<String, MIMEMappingItem>();
71      public static final String DEFAULT_CHAR_ENCODING = "UTF-8";
72      public static final String DEFAULT_EXTENSION = "html";
73  
74      /**
75       * Used to keep the configuration in memory.
76       */
77      protected static class MIMEMappingItem {
78  
79          protected String ext;
80  
81          protected String mime;
82  
83          protected String icon;
84  
85          protected String iconStyle;
86      }
87  
88  
89      /**
90       * Utility class, don't instantiate.
91       */
92      private MIMEMapping() {
93          // unused
94      }
95  
96      /**
97       * Reads all configured mime mapping (config/server/MIMEMapping).
98       */
99      public static void init() {
100         log.info("Initializing MIMEMapping from {}", NODEPATH);
101         load();
102         registerEventListener();
103     }
104 
105     /**
106      * Reads all configured mime mapping (config/server/MIMEMapping).
107      */
108     public static void load() {
109         MIMEMapping.cachedContent.clear();
110         try {
111             final Session session = MgnlContext.getSystemContext().getJCRSession(RepositoryConstants.CONFIG);
112 
113             final Iterable<Node> mimeList = NodeUtil.getNodes(session.getNode(NODEPATH), NodeTypes.ContentNode.NAME);
114             MIMEMapping.cacheContent(mimeList.iterator());
115             log.debug("MIMEMapping loaded from {}", NODEPATH);
116         } catch (PathNotFoundException e) {
117             log.warn("No MIMEMapping info configured at {}", NODEPATH);
118         } catch (RepositoryException e) {
119             log.error("Failed to load MIMEMapping: {}", e.getMessage(), e);
120         }
121     }
122 
123     public static void reload() {
124         log.info("Reloading MIMEMapping from {}", NODEPATH);
125         MIMEMapping.load();
126     }
127 
128     /**
129      * Register an event listener: reload cache configuration when something changes.
130      */
131     private static void registerEventListener() {
132         log.info("Registering event listener for MIMEMapping");
133 
134         try {
135             WorkspaceEventListenerRegistration.observe(RepositoryConstants.CONFIG, NODEPATH, eventIterator -> {
136                 // reload everything
137                 reload();
138             }).register();
139         } catch (RepositoryException e) {
140             log.error("An error occurred while registering event listener for MIMEMapping", e);
141         }
142     }
143 
144     /**
145      * Cache all MIME types configured.
146      */
147     private static void cacheContent(Iterator<Node> iterator) {
148         while (iterator.hasNext()) {
149             Node c = iterator.next();
150             try {
151                 MIMEMappingItem item = new MIMEMappingItem();
152                 item.ext = PropertyUtil.getString(c, "extension", c.getName());
153                 item.mime = PropertyUtil.getString(c, "mime-type");
154                 item.icon = PropertyUtil.getString(c, "icon");
155                 item.iconStyle = PropertyUtil.getString(c, "iconStyle");
156 
157                 MIMEMapping.cachedContent.put(item.ext, item);
158             } catch (Exception e) {
159                 log.error("Failed to cache MIMEMapping");
160             }
161         }
162     }
163 
164     /**
165      * Get MIME type String.
166      *
167      * @param key extension for which MIME type is requested
168      * @return MIME type
169      */
170     public static String getMIMEType(String key) {
171         if (StringUtils.isEmpty(key)) {
172             return StringUtils.EMPTY;
173         }
174         // check that the cached content contains the key first to avoid NPE when accessing 'mime'
175         String loweredKey = key.toLowerCase();
176         if (MIMEMapping.cachedContent.containsKey(loweredKey)) {
177             return MIMEMapping.cachedContent.get(loweredKey).mime;
178         }
179 
180         // this is expected by the caller
181         return null;
182 
183     }
184 
185     /**
186      * Returns the mime-type associated with this extension, or the server's default.
187      */
188     public static String getMIMETypeOrDefault(String extension) {
189         String mimeType = getMIMEType(extension);
190 
191         if (StringUtils.isNotEmpty(mimeType)) {
192             return mimeType;
193         }
194 
195         if (StringUtils.isNotEmpty(extension)) {
196             log.info("Cannot find MIME type for extension \"{}\"", extension);
197         }
198 
199         String defaultExtension = ServerConfiguration.getInstance().getDefaultExtension();
200         if (StringUtils.isBlank(defaultExtension)) {
201             defaultExtension = DEFAULT_EXTENSION;
202         }
203         return getMIMEType(defaultExtension);
204     }
205 
206     public static String getContentEncoding(String contentType) {
207         if (contentType != null) {
208             int index = contentType.lastIndexOf(";");
209             if (index > -1) {
210                 String encoding = contentType.substring(index + 1).toLowerCase().trim();
211                 encoding = encoding.replaceAll("charset=", StringUtils.EMPTY);
212                 return encoding;
213             }
214         }
215         return StringUtils.EMPTY;
216     }
217 
218     public static String getContentEncodingOrDefault(String contentType) {
219         final String characterEncoding = getContentEncoding(contentType);
220 
221         return (StringUtils.isEmpty(characterEncoding)) ? DEFAULT_CHAR_ENCODING : characterEncoding;
222     }
223 
224     /**
225      * Returns the icon used for rendering this type.
226      *
227      * @return the icon name
228      */
229     public static String getMIMETypeIcon(String extension) {
230         MIMEMappingItem item = MIMEMapping.cachedContent.get(extension.toLowerCase());
231         if (item != null) {
232             return StringUtils.defaultIfEmpty(item.icon, DEFAULT_ICON);
233         }
234 
235         return DEFAULT_ICON;
236 
237     }
238 
239     /**
240      * Returns the icon css style name.
241      *
242      * @return icon css style name.
243      */
244     public static String getMIMETypeIconStyle(String mimeType) {
245         String genericMimeType = StringUtils.substringBefore(mimeType, "/");
246         MIMEMappingItem mimeMappingItem = null;
247         for (MIMEMappingItem item : cachedContent.values()) {
248             if (item.mime != null && item.mime.equals(mimeType) && StringUtils.isNotBlank(item.iconStyle)) {
249                 mimeMappingItem = item;
250                 break;
251             }
252             if (item.mime != null && item.mime.equals(genericMimeType) && StringUtils.isNotBlank(item.iconStyle)) {
253                 mimeMappingItem = item;
254             }
255         }
256         if (mimeMappingItem != null && StringUtils.isNotBlank(mimeMappingItem.iconStyle)) {
257             return mimeMappingItem.iconStyle;
258         }
259         return DEFAULT_ICON_STYLE;
260     }
261 }