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