View Javadoc
1   /**
2    * This file Copyright (c) 2012-2017 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 LAST_ACTIVATED_VERSION = LAST_ACTIVATED + "Version";
141         public static final String ACTIVATION_STATUS = MGNL_PREFIX + "activationStatus";
142 
143         public static final int ACTIVATION_STATUS_NOT_ACTIVATED = 0;
144 
145         public static final int ACTIVATION_STATUS_MODIFIED = 1;
146 
147         public static final int ACTIVATION_STATUS_ACTIVATED = 2;
148 
149         /**
150          * Returns the activation status of the node. Returns one of the constants:
151          * <ul>
152          * <li>{@link #ACTIVATION_STATUS_NOT_ACTIVATED} if the node has not been activated</li>
153          * <li>{@link #ACTIVATION_STATUS_MODIFIED} has been activated and subsequently modified</li>
154          * <li>{@link #ACTIVATION_STATUS_ACTIVATED} has been activated and not modified since</li>
155          * </ul>
156          */
157         public static int getActivationStatus(Node node) throws RepositoryException {
158 
159             if (!isActivated(node)) {
160                 // never activated or deactivated
161                 return ACTIVATION_STATUS_NOT_ACTIVATED;
162             }
163 
164             Calendar lastModified = LastModified.getLastModified(node);
165             Calendar lastActivated = getLastActivated(node);
166 
167             if (lastModified != null && lastModified.after(lastActivated)) {
168                 // node has been modified after last activation
169                 return ACTIVATION_STATUS_MODIFIED;
170             }
171 
172             // activated and not modified ever since
173             return ACTIVATION_STATUS_ACTIVATED;
174         }
175 
176         /**
177          * Returns true if the node has been activated.
178          */
179         public static boolean isActivated(Node node) throws RepositoryException {
180             return node.hasProperty(ACTIVATION_STATUS) && node.getProperty(ACTIVATION_STATUS).getBoolean();
181         }
182 
183         /**
184          * Returns the date when the node was last activated or null if no activation date has been stored on the node.
185          */
186         public static Calendar getLastActivated(Node node) throws RepositoryException {
187             return node.hasProperty(LAST_ACTIVATED) ? node.getProperty(LAST_ACTIVATED).getDate() : null;
188         }
189 
190         /**
191          * Returns the name of the user that last activated the node or null if no activating user has been stored on the node.
192          */
193         public static String getLastActivatedBy(Node node) throws RepositoryException {
194             return node.hasProperty(LAST_ACTIVATED_BY) ? node.getProperty(LAST_ACTIVATED_BY).getString() : null;
195         }
196 
197         /**
198          * Returns the latest activated version name or null if the version name isn't set.
199          */
200         public static String getLastActivatedVersion(Node node) throws RepositoryException {
201             return node.hasProperty(LAST_ACTIVATED_VERSION) ? node.getProperty(LAST_ACTIVATED_VERSION).getString() : null;
202         }
203 
204         /**
205          * Sets the name of the user that performed the most recent activation as well as to current time.
206          */
207         public static void update(Node node, String userName, boolean isActivated) throws RepositoryException {
208             update(node, userName, isActivated, null);
209         }
210 
211         /**
212          * Sets the name of the user that performed the most recent activation as well as to current time and last activated version name.
213          */
214         public static void update(Node node, String userName, boolean isActivated, String versionName) throws RepositoryException {
215             checkNodeType(node, Activatable.NAME, LAST_ACTIVATED, LAST_ACTIVATED_BY, ACTIVATION_STATUS, LAST_ACTIVATED_VERSION);
216             node.setProperty(LAST_ACTIVATED, getCurrentCalendar());
217             node.setProperty(LAST_ACTIVATED_BY, userName);
218             node.setProperty(ACTIVATION_STATUS, isActivated);
219             node.setProperty(LAST_ACTIVATED_VERSION, versionName);
220         }
221 
222     }
223 
224     /**
225      * Represents the mixin mgnl:created.
226      */
227     public static class Created {
228         public static final String NAME = MGNL_PREFIX + "created";
229         public static final String CREATED = NAME;
230         public static final String CREATED_BY = CREATED + BY;
231 
232         /**
233          * Returns the creation date of a node or null if creation date isn't set.
234          */
235         public static Calendar getCreated(Node node) throws RepositoryException {
236             return node.hasProperty(CREATED) ? node.getProperty(CREATED).getDate() : null;
237         }
238 
239         /**
240          * Returns the name of the user that created a node.
241          */
242         public static String getCreatedBy(Node node) throws RepositoryException {
243             return node.hasProperty(CREATED_BY) ? node.getProperty(CREATED_BY).getString() : null;
244         }
245 
246         /**
247          * Sets the current date as the node's creation date and uses {@link info.magnolia.context.MgnlContext} to set the name of the creating
248          * user. Used with nodes having the <code>mgnl:created</code> mixin.
249          */
250         public static void set(Node node) throws RepositoryException {
251             set(node, getCurrentUserName(), getCurrentCalendar());
252         }
253 
254         /**
255          * Sets the supplied date as the node's creation date and sets the name of the creating user. Also sets the date of
256          * modification and the user last having modified the node to the same values. Used with nodes having the
257          * <code>mgnl:created</code> mixin.
258          */
259         public static void set(Node node, String userName, Calendar created) throws RepositoryException {
260             checkNodeType(node, NAME, CREATED, CREATED_BY);
261             node.setProperty(CREATED, created);
262             node.setProperty(CREATED_BY, userName);
263 
264             LastModified.update(node, userName, created);
265         }
266     }
267 
268     /**
269      * Represents the mixin mgnl:renderable.
270      */
271     public static class Renderable {
272         public static final String NAME = MGNL_PREFIX + "renderable";
273         public static final String TEMPLATE = MGNL_PREFIX + "template";
274 
275         /**
276          * Returns the template assigned to the node or null of none has been assigned. Used with nodes having the
277          * <code>mgnl:renderable</code> mixin.
278          */
279         public static String getTemplate(Node node) throws RepositoryException {
280             return node.hasProperty(TEMPLATE) ? node.getProperty(TEMPLATE).getString() : null;
281         }
282 
283         /**
284          * Sets the template assigned to the node. Used with nodes having the <code>mgnl:renderable</code> mixin.
285          */
286         public static void set(Node node, String template) throws RepositoryException {
287             checkNodeType(node, NAME, TEMPLATE);
288             node.setProperty(TEMPLATE, template);
289         }
290     }
291 
292     /**
293      * Represents the mixin mgnl:deleted.
294      */
295     public static class Deleted {
296         public static final String NAME = MGNL_PREFIX + "deleted";
297         public static final String DELETED = NAME;
298         public static final String DELETED_BY = DELETED + BY;
299         public static final String COMMENT = MGNL_PREFIX + "comment";
300 
301         /**
302          * Returns the date when the node was deleted or null if no deletion date has been stored on the node.
303          */
304         public static Calendar getDeleted(Node node) throws RepositoryException {
305             return node.hasProperty(DELETED) ? node.getProperty(DELETED).getDate() : null;
306         }
307 
308         /**
309          * Returns the name of the user that deleted the node or null if no deleting user has been stored on the node.
310          */
311         public static String getDeletedBy(Node node) throws RepositoryException {
312             return node.hasProperty(DELETED_BY) ? node.getProperty(DELETED_BY).getString() : null;
313         }
314 
315         /**
316          * Returns the comment set when then node was last deleted or null if no comment has been set.
317          */
318         public static String getComment(Node node) throws RepositoryException {
319             return node.hasProperty(COMMENT) ? node.getProperty(COMMENT).getString() : null;
320         }
321 
322         public static void set(Node node, String comment) throws RepositoryException {
323             checkNodeType(node, NAME, DELETED, DELETED_BY, COMMENT);
324             node.setProperty(DELETED, getCurrentCalendar());
325             node.setProperty(DELETED_BY, getCurrentUserName());
326             node.setProperty(COMMENT, comment);
327         }
328     }
329 
330     /**
331      * Represents the mixin mgnl:versionable.
332      */
333     public static class Versionable {
334         public static final String NAME = MGNL_PREFIX + "versionable";
335         public static final String COMMENT = Deleted.COMMENT;
336 
337         /**
338          * Returns the comment set when then node was last versioned or null if no comment has been set.
339          */
340         public static String getComment(Node node) throws RepositoryException {
341             return node.hasProperty(COMMENT) ? node.getProperty(COMMENT).getString() : null;
342         }
343 
344         /**
345          * Set the versioning comment on the node.
346          */
347         public static void set(Node node, String comment) throws RepositoryException {
348             checkNodeType(node, NAME, COMMENT);
349             node.setProperty(COMMENT, comment);
350         }
351     }
352 
353     /**
354      * Represents the nodeType mgnl:folder.
355      */
356     public static class Folder {
357         public static final String NAME = MGNL_PREFIX + "folder";
358     }
359 
360     /**
361      * Represents the nodeType mgnl:resource.
362      */
363     public static class Resource {
364         public static final String NAME = MGNL_PREFIX + "resource";
365     }
366 
367     /**
368      * Represents the nodeType mgnl:content.
369      */
370     public static class Content {
371         public static final String NAME = MGNL_PREFIX + "content";
372     }
373 
374     /**
375      * Represents the nodeType mgnl:contentNode.
376      */
377     public static class ContentNode {
378         public static final String NAME = MGNL_PREFIX + "contentNode";
379     }
380 
381     /**
382      * Represents the nodeType mgnl:nodeData.
383      */
384     public static class NodeData {
385         public static final String NAME = MGNL_PREFIX + "nodeData";
386     }
387 
388     /**
389      * Represents the nodeType mgnl:page.
390      */
391     public static class Page {
392         public static final String NAME = MGNL_PREFIX + "page";
393     }
394 
395     /**
396      * Represents the nodeType mgnl:area.
397      */
398     public static class Area {
399         public static final String NAME = MGNL_PREFIX + "area";
400     }
401 
402     /**
403      * Represents the nodeType mgnl:component.
404      */
405     public static class Component {
406         public static final String NAME = MGNL_PREFIX + "component";
407     }
408 
409     /**
410      * Represents the nodeType mgnl:user.
411      */
412     public static class User {
413         public static final String NAME = MGNL_PREFIX + "user";
414     }
415 
416     /**
417      * Represents the nodeType mgnl:role.
418      */
419     public static class Role {
420         public static final String NAME = MGNL_PREFIX + "role";
421     }
422 
423     /**
424      * Represents the nodeType mgnl:group.
425      */
426     public static class Group {
427         public static final String NAME = MGNL_PREFIX + "group";
428     }
429 
430     /**
431      * Represents the nodeType mgnl:reserve.
432      */
433     public static class System {
434         public static final String NAME = MGNL_PREFIX + "reserve";
435     }
436 
437     /**
438      * Represents the nodeType mgnl:metaData.
439      * Is basically obsolete since MetaData as mixin but could still be used in customers code,
440      * hence it has to stay for quite a while.
441      */
442     public static class MetaData {
443         public static final String NAME = MGNL_PREFIX + "metaData";
444     }
445 
446     /**
447      * Mixin that marks node that have versions.
448      */
449     public static class HasVersion {
450         public static final String NAME = MGNL_PREFIX + "hasVersion";
451     }
452 
453     protected static String getCurrentUserName() {
454         String userName = MgnlContext.getUser().getName();
455         if (MgnlContext.isSystemInstance()) {
456             // in system context try to obtain original non-system context and retrieve user from it
457             Context ctx = ((SystemContext) MgnlContext.getInstance()).getOriginalContext();
458             if (ctx != null && ctx.getUser() != null && !userName.equals(ctx.getUser().getName())) {
459                 // 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
460                 return "System [" + ctx.getUser().getName() + "]";
461             }
462         }
463         return userName;
464     }
465 
466     protected static Calendar getCurrentCalendar() {
467         return Calendar.getInstance();
468     }
469 
470     public static void checkNodeType(Node node, String nodeType, String... propertyNames) throws RepositoryException {
471         if (!node.isNodeType(nodeType)) {
472             log.warn("Trying to set property/ies '{}' although the node '{}' with PrimaryType '{}' is not of type '{}'!", propertyNames, node.getPath(), node.getPrimaryNodeType().getName(), nodeType);
473         }
474     }
475 }