View Javadoc

1   /**
2    * This file Copyright (c) 2012-2013 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.jcr.util;
35  
36  import info.magnolia.context.MgnlContext;
37  
38  import java.util.Calendar;
39  
40  import javax.jcr.Node;
41  import javax.jcr.RepositoryException;
42  
43  import org.apache.commons.lang.StringUtils;
44  import org.slf4j.Logger;
45  import org.slf4j.LoggerFactory;
46  
47  /**
48   * Magnolia defined NodeTypes together with their properties and some convenience methods.
49   */
50  public class NodeTypes {
51  
52      private static final Logger log = LoggerFactory.getLogger(NodeTypes.class);
53  
54      /** Namespace for Magnolia extensions. */
55      public static final String MGNL_PREFIX = "mgnl:";
56  
57      /** Namespace for jcr nodes and properties. */
58      public static final String JCR_PREFIX = "jcr:";
59  
60      /** Namespace for rep nodes and properties. */
61      public static final String REP_PREFIX = "rep:";
62  
63      /** Default suffix for userName keeping properties. */
64      private static final String BY = "By";
65  
66      /**
67       * Represents the mixin mgnl:lastModified.
68       */
69      public static class LastModified {
70          public static final String NAME = MGNL_PREFIX + "lastModified";
71          public static final String LAST_MODIFIED = NAME;
72          public static final String LAST_MODIFIED_BY = LAST_MODIFIED + BY;
73  
74          /**
75           * Returns the date when this node was last modified. If the no modification date has been stored on the node this
76           * method return the creation date if set, otherwise null is returned.
77           */
78          public static Calendar getLastModified(Node node) throws RepositoryException {
79              return node.hasProperty(LAST_MODIFIED) ? node.getProperty(LAST_MODIFIED).getDate() : Created.getCreated(node);
80          }
81  
82          /**
83           * Returns the name of the user that last modified the node. If no modification has been stored on the node
84           * this method return the name of the user that created the node if set, otherwise null is returned.
85           */
86          public static String getLastModifiedBy(Node node) throws RepositoryException {
87              return node.hasProperty(LAST_MODIFIED_BY) ? node.getProperty(LAST_MODIFIED_BY).getString() : Created.getCreatedBy(node);
88          }
89  
90          /**
91           * Sets the date of modification to current Calendar and uses {@link info.magnolia.context.MgnlContext} to set the name of the user.
92           * It should not be necessary to call this method explicitly as all Magnolia treated node instances are self updating the property defined by mixin.
93           * 
94           * @see info.magnolia.jcr.wrapper.LastUpdateContentDecorator for more details.
95           */
96          public static void update(Node node) throws RepositoryException {
97              update(node, getCurrentUserName(), getCurrentCalendar());
98          }
99  
100         /**
101          * Sets the date of modification and the name of the user modifying a node.
102          * It should not be necessary to call this method explicitly as all Magnolia treated node instances are self updating the property defined by mixin.
103          * 
104          * @see info.magnolia.jcr.wrapper.LastUpdateContentDecorator for more details.
105          */
106         public static void update(Node node, String userName, Calendar lastModified) throws RepositoryException {
107             checkNodeType(node, LastModified.NAME, LAST_MODIFIED, LAST_MODIFIED_BY);
108             node.setProperty(LAST_MODIFIED, lastModified);
109             node.setProperty(LAST_MODIFIED_BY, userName);
110         }
111 
112     }
113 
114     /**
115      * Represents the mixin mgnl:activatable.
116      */
117     public static class Activatable {
118         public static final String NAME = MGNL_PREFIX + "activatable";
119         public static final String LAST_ACTIVATED = MGNL_PREFIX + "lastActivated";
120         public static final String LAST_ACTIVATED_BY = LAST_ACTIVATED + BY;
121         public static final String ACTIVATION_STATUS = MGNL_PREFIX + "activationStatus";
122 
123         public static final int ACTIVATION_STATUS_NOT_ACTIVATED = 0;
124 
125         public static final int ACTIVATION_STATUS_MODIFIED = 1;
126 
127         public static final int ACTIVATION_STATUS_ACTIVATED = 2;
128 
129         /**
130          * Returns the activation status of the node. Returns one of the constants:
131          * <ul>
132          * <li>{@link #ACTIVATION_STATUS_NOT_ACTIVATED} if the node has not been activated</li>
133          * <li>{@link #ACTIVATION_STATUS_MODIFIED} has been activated and subsequently modified</li>
134          * <li>{@link #ACTIVATION_STATUS_ACTIVATED} has been activated and not modified since</li>
135          * </ul>
136          */
137         public static int getActivationStatus(Node node) throws RepositoryException {
138 
139             if (!isActivated(node)) {
140                 // never activated or deactivated
141                 return ACTIVATION_STATUS_NOT_ACTIVATED;
142             }
143 
144             Calendar lastModified = LastModified.getLastModified(node);
145             Calendar lastActivated = getLastActivated(node);
146 
147             if (lastModified != null && lastModified.after(lastActivated)) {
148                 // node has been modified after last activation
149                 return ACTIVATION_STATUS_MODIFIED;
150             }
151 
152             // activated and not modified ever since
153             return ACTIVATION_STATUS_ACTIVATED;
154         }
155 
156         /**
157          * Returns true if the node has been activated.
158          */
159         public static boolean isActivated(Node node) throws RepositoryException {
160             return node.hasProperty(ACTIVATION_STATUS) && node.getProperty(ACTIVATION_STATUS).getBoolean();
161         }
162 
163         /**
164          * Returns the date when the node was last activated or null if no activation date has been stored on the node.
165          */
166         public static Calendar getLastActivated(Node node) throws RepositoryException {
167             return node.hasProperty(LAST_ACTIVATED) ? node.getProperty(LAST_ACTIVATED).getDate() : null;
168         }
169 
170         /**
171          * Returns the name of the user that last activated the node or null if no activating user has been stored on the node.
172          */
173         public static String getLastActivatedBy(Node node) throws RepositoryException {
174             return node.hasProperty(LAST_ACTIVATED_BY) ? node.getProperty(LAST_ACTIVATED_BY).getString() : null;
175         }
176 
177         /**
178          * Sets the name of the user that performed the most recent activation as well as to current time.
179          */
180         public static void update(Node node, String userName, boolean isActivated) throws RepositoryException {
181             checkNodeType(node, Activatable.NAME, LAST_ACTIVATED, LAST_ACTIVATED_BY, ACTIVATION_STATUS);
182             node.setProperty(LAST_ACTIVATED, getCurrentCalendar());
183             node.setProperty(LAST_ACTIVATED_BY, userName);
184             node.setProperty(ACTIVATION_STATUS, isActivated);
185         }
186 
187     }
188 
189     /**
190      * Represents the mixin mgnl:created.
191      */
192     public static class Created {
193         public static final String NAME = MGNL_PREFIX + "created";
194         public static final String CREATED = NAME;
195         public static final String CREATED_BY = CREATED + BY;
196 
197         /**
198          * Returns the creation date of a node or null if creation date isn't set.
199          */
200         public static Calendar getCreated(Node node) throws RepositoryException {
201             return node.hasProperty(CREATED) ? node.getProperty(CREATED).getDate() : null;
202         }
203 
204         /**
205          * Returns the name of the user that created a node.
206          */
207         public static String getCreatedBy(Node node) throws RepositoryException {
208             return node.hasProperty(CREATED_BY) ? node.getProperty(CREATED_BY).getString() : null;
209         }
210 
211         /**
212          * Sets the current date as the node's creation date and uses {@link info.magnolia.context.MgnlContext} to set the name of the creating
213          * user. Used with nodes having the <code>mgnl:created</code> mixin.
214          */
215         public static void set(Node node) throws RepositoryException {
216             set(node, getCurrentUserName(), getCurrentCalendar());
217         }
218 
219         /**
220          * Sets the supplied date as the node's creation date and sets the name of the creating user. Also sets the date of
221          * modification and the user last having modified the node to the same values. Used with nodes having the
222          * <code>mgnl:created</code> mixin.
223          */
224         static void set(Node node, String userName, Calendar created) throws RepositoryException {
225             checkNodeType(node, NAME, CREATED, CREATED_BY);
226             node.setProperty(CREATED, created);
227             node.setProperty(CREATED_BY, userName);
228 
229             LastModified.update(node, userName, created);
230         }
231     }
232 
233     /**
234      * Represents the mixin mgnl:renderable.
235      */
236     public static class Renderable {
237         public static final String NAME = MGNL_PREFIX + "renderable";
238         public static final String TEMPLATE = MGNL_PREFIX + "template";
239 
240         /**
241          * Returns the template assigned to the node or null of none has been assigned. Used with nodes having the
242          * <code>mgnl:renderable</code> mixin.
243          */
244         public static String getTemplate(Node node) throws RepositoryException {
245             return node.hasProperty(TEMPLATE) ? node.getProperty(TEMPLATE).getString() : null;
246         }
247 
248         /**
249          * Sets the template assigned to the node. Used with nodes having the <code>mgnl:renderable</code> mixin.
250          */
251         public static void set(Node node, String template) throws RepositoryException {
252             checkNodeType(node, NAME, TEMPLATE);
253             node.setProperty(TEMPLATE, template);
254         }
255     }
256 
257     /**
258      * Represents the mixin mgnl:deleted.
259      */
260     public static class Deleted {
261         public static final String NAME = MGNL_PREFIX + "deleted";
262         public static final String DELETED = NAME;
263         public static final String DELETED_BY = DELETED + BY;
264         public static final String COMMENT = MGNL_PREFIX + "comment";
265 
266         /**
267          * Returns the date when the node was deleted or null if no deletion date has been stored on the node.
268          */
269         public static Calendar getDeleted(Node node) throws RepositoryException {
270             return node.hasProperty(DELETED) ? node.getProperty(DELETED).getDate() : null;
271         }
272 
273         /**
274          * Returns the name of the user that deleted the node or null if no deleting user has been stored on the node.
275          */
276         public static String getDeletedBy(Node node) throws RepositoryException {
277             return node.hasProperty(DELETED_BY) ? node.getProperty(DELETED_BY).getString() : null;
278         }
279 
280         /**
281          * Returns the comment set when then node was last deleted or null if no comment has been set.
282          */
283         public static String getComment(Node node) throws RepositoryException {
284             return node.hasProperty(COMMENT) ? node.getProperty(COMMENT).getString() : null;
285         }
286 
287         public static void set(Node node, String comment) throws RepositoryException {
288             checkNodeType(node, NAME, DELETED, DELETED_BY, COMMENT);
289             node.setProperty(DELETED, getCurrentCalendar());
290             node.setProperty(DELETED_BY, getCurrentUserName());
291             node.setProperty(COMMENT, comment);
292         }
293     }
294 
295     /**
296      * Represents the mixin mgnl:versionable.
297      */
298     public static class Versionable {
299         public static final String NAME = MGNL_PREFIX + "versionable";
300         public static final String COMMENT = Deleted.COMMENT;
301 
302         /**
303          * Returns the comment set when then node was last versioned or null if no comment has been set.
304          */
305         public static String getComment(Node node) throws RepositoryException {
306             return node.hasProperty(COMMENT) ? node.getProperty(COMMENT).getString() : null;
307         }
308 
309         /**
310          * Set the versioning comment on the node.
311          */
312         public static void set(Node node, String comment) throws RepositoryException{
313             checkNodeType(node, NAME, COMMENT);
314             node.setProperty(COMMENT, comment);
315         }
316     }
317 
318     /**
319      * Represents the nodeType mgnl:folder.
320      */
321     public static class Folder {
322         public static final String NAME = MGNL_PREFIX + "folder";
323     }
324 
325     /**
326      * Represents the nodeType mgnl:resource.
327      */
328     public static class Resource {
329         public static final String NAME = MGNL_PREFIX + "resource";
330     }
331 
332     /**
333      * Represents the nodeType mgnl:content.
334      */
335     public static class Content {
336         public static final String NAME = MGNL_PREFIX + "content";
337     }
338 
339     /**
340      * Represents the nodeType mgnl:contentNode.
341      */
342     public static class ContentNode {
343         public static final String NAME = MGNL_PREFIX + "contentNode";
344     }
345 
346     /**
347      * Represents the nodeType mgnl:nodeData.
348      */
349     public static class NodeData {
350         public static final String NAME = MGNL_PREFIX + "nodeData";
351     }
352 
353     /**
354      * Represents the nodeType mgnl:page.
355      */
356     public static class Page {
357         public static final String NAME = MGNL_PREFIX + "page";
358     }
359 
360     /**
361      * Represents the nodeType mgnl:area.
362      */
363     public static class Area {
364         public static final String NAME = MGNL_PREFIX + "area";
365     }
366 
367     /**
368      * Represents the nodeType mgnl:component.
369      */
370     public static class Component {
371         public static final String NAME = MGNL_PREFIX + "component";
372     }
373 
374     /**
375      * Represents the nodeType mgnl:user.
376      */
377     public static class User {
378         public static final String NAME = MGNL_PREFIX + "user";
379     }
380 
381     /**
382      * Represents the nodeType mgnl:role.
383      */
384     public static class Role {
385         public static final String NAME = MGNL_PREFIX + "role";
386     }
387 
388     /**
389      * Represents the nodeType mgnl:group.
390      */
391     public static class Group {
392         public static final String NAME = MGNL_PREFIX + "group";
393     }
394 
395     /**
396      * Represents the nodeType mgnl:reserve.
397      */
398     public static class System {
399         public static final String NAME = MGNL_PREFIX + "reserve";
400     }
401 
402     /**
403      * Represents the nodeType mgnl:metaData.
404      * Is basically obsolete since MetaData as mixin but could still be used in customers code,
405      * hence it has to stay for quite a while.
406      */
407     public static class MetaData {
408         public static final String NAME = MGNL_PREFIX + "metaData";
409     }
410 
411     protected static String getCurrentUserName() {
412         return MgnlContext.getUser() == null ? "not available" : MgnlContext.getUser().getName();
413     }
414 
415     protected static Calendar getCurrentCalendar() {
416         return Calendar.getInstance();
417     }
418 
419     public static void checkNodeType(Node node, String nodeType, String... propertyNames) throws RepositoryException {
420         if (!node.isNodeType(nodeType)) {
421             log.warn("Trying to set property/ies '" + StringUtils.join(propertyNames, ", ") + "' although the node '" + node.getPath() + "' with PrimaryType '" + node.getPrimaryNodeType().getName() + "' is not of type '" + nodeType + "'!");
422         }
423     }
424 }