View Javadoc

1   /**
2    * This file Copyright (c) 2010-2011 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.core;
35  
36  import info.magnolia.cms.i18n.I18nContentSupportFactory;
37  import info.magnolia.cms.security.AccessDeniedException;
38  import info.magnolia.cms.security.PermissionUtil;
39  import info.magnolia.cms.util.NodeDataUtil;
40  import info.magnolia.cms.util.NodeTypeFilter;
41  import info.magnolia.context.MgnlContext;
42  import info.magnolia.logging.AuditLoggingUtil;
43  
44  import java.io.InputStream;
45  import java.util.ArrayList;
46  import java.util.Calendar;
47  import java.util.Collection;
48  import java.util.Comparator;
49  
50  import javax.jcr.PathNotFoundException;
51  import javax.jcr.PropertyType;
52  import javax.jcr.RepositoryException;
53  import javax.jcr.Value;
54  import javax.jcr.Workspace;
55  
56  import org.apache.commons.lang.StringUtils;
57  
58  /**
59   * A base class by implementing some default behavior.
60   * A subclass must carefully implement {@link #newNodeDataInstance(String, int, boolean)},
61   * {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, String, java.util.Comparator)} and
62   * {@link #getNodeDataCollection(String)}.
63   *
64   * @author pbaerfuss
65   * @version $Id$
66   */
67  public abstract class AbstractContent extends ContentHandler implements Content {
68      private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractContent.class);
69  
70      @Override
71      public Content createContent(String name) throws PathNotFoundException, RepositoryException, AccessDeniedException {
72          return createContent(name, ItemType.CONTENT);
73      }
74  
75      @Override
76      public Content createContent(String name, ItemType contentType) throws PathNotFoundException, RepositoryException, AccessDeniedException {
77          return createContent(name, contentType.getSystemName());
78      }
79  
80      @Override
81      public NodeData createNodeData(String name) throws PathNotFoundException, RepositoryException, AccessDeniedException {
82          return setNodeData(name, "");
83      }
84  
85      @Override
86      public NodeData createNodeData(String name, Value value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
87          return setNodeData(name, value);
88      }
89  
90      /**
91       * @deprecated
92       */
93      @Override
94      @Deprecated
95      public NodeData createNodeData(String name, Value[] value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
96          return setNodeData(name, value);
97      }
98  
99      /**
100      * @deprecated
101      */
102     @Override
103     @Deprecated
104     public NodeData createNodeData(String name, int type) throws PathNotFoundException, RepositoryException, AccessDeniedException {
105         // set some default values to create the property
106         switch (type) {
107         case PropertyType.STRING:
108             return setNodeData(name, StringUtils.EMPTY);
109         case PropertyType.BOOLEAN:
110             return setNodeData(name, Boolean.FALSE);
111         case PropertyType.DATE:
112             return setNodeData(name, Calendar.getInstance());
113         case PropertyType.LONG:
114             return setNodeData(name, Long.valueOf(0));
115         case PropertyType.DOUBLE:
116             return setNodeData(name, Double.valueOf(0.0));
117         default:
118             return newNodeDataInstance(name, type, true);
119         }
120     }
121 
122     /**
123      * @deprecated
124      */
125     @Override
126     @Deprecated
127     public NodeData createNodeData(String name, Object valueObj) throws RepositoryException {
128         return setNodeData(name, valueObj);
129     }
130 
131     /**
132      * {@inheritDoc}
133      * Delegates to {@link #newNodeDataInstance(String, int, boolean)} by setting the type to PropertyType.UNDEFINED. A subclass has to handle this by trying to determine the type if the node data exists. The reason for this is that implementations want to instantiate different node data classes per type
134      */
135     @Override
136     public NodeData getNodeData(String name) {
137         try {
138             // will try to determine the type if the node data exists, otherwise an non-mutable node data will be returned
139             return newNodeDataInstance(name, PropertyType.UNDEFINED, false);
140         }
141         catch(RepositoryException e){
142             throw new IllegalStateException("Can't instantiate node data " + name + " on node " + toString(), e);
143         }
144     }
145 
146     /**
147      * As defined in {@link Content#getNodeData(String)} this method always returns a node data object. If the type is {@link PropertyType#UNDEFINED} the implementation should check if the node data exists and determine the type to use.
148      *
149      * @param createIfNotExisting if false an empty non-mutable node data will be returned if the node data doesn't exist otherwise a mutable nodedata object is returned (depending on the type)
150      */
151     abstract public NodeData newNodeDataInstance(String name, int type, boolean createIfNotExisting) throws AccessDeniedException, RepositoryException;
152 
153     /**
154      * Delegates to {@link NodeData#isExist()}.
155      */
156     @Override
157     public boolean hasNodeData(String name) throws RepositoryException {
158         return getNodeData(name).isExist();
159     }
160 
161     @Override
162     public NodeData setNodeData(String name, Value value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
163         NodeData nodeData = newNodeDataInstance(name, value.getType(), true);
164         nodeData.setValue(value);
165         return nodeData;
166     }
167 
168     @Override
169     public NodeData setNodeData(String name, Value[] value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
170         if(value.length == 0){
171             throw new IllegalArgumentException("Value array can't be empty");
172         }
173         NodeData nodeData = newNodeDataInstance(name, value[0].getType(), true);
174         nodeData.setValue(value);
175         return nodeData;
176     }
177 
178     @Override
179     public NodeData setNodeData(String name, boolean value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
180         NodeData nodeData = newNodeDataInstance(name, PropertyType.BOOLEAN, true);
181         nodeData.setValue(value);
182         return nodeData;
183     }
184 
185     @Override
186     public NodeData setNodeData(String name, long value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
187         NodeData nodeData = newNodeDataInstance(name, PropertyType.LONG, true);
188         nodeData.setValue(value);
189         return nodeData;
190     }
191 
192     @Override
193     public NodeData setNodeData(String name, double value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
194         NodeData nodeData = newNodeDataInstance(name, PropertyType.DOUBLE, true);
195         nodeData.setValue(value);
196         return nodeData;
197     }
198 
199     @Override
200     public NodeData setNodeData(String name, String value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
201         NodeData nodeData = newNodeDataInstance(name, PropertyType.STRING, true);
202         nodeData.setValue(value);
203         return nodeData;
204     }
205 
206     @Override
207     public NodeData setNodeData(String name, InputStream value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
208         NodeData nodeData = newNodeDataInstance(name, PropertyType.BINARY, true);
209         nodeData.setValue(value);
210         return nodeData;
211     }
212 
213     @Override
214     public NodeData setNodeData(String name, Calendar value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
215         NodeData nodeData = newNodeDataInstance(name, PropertyType.DATE, true);
216         nodeData.setValue(value);
217         return nodeData;
218     }
219 
220     @Override
221     public NodeData setNodeData(String name, Content value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
222         NodeData nodeData = newNodeDataInstance(name, PropertyType.REFERENCE, true);
223         nodeData.setValue(value);
224         return nodeData;
225     }
226 
227     /**
228      * Uses the {@link NodeDataUtil} to create and set the node data based on the object type.
229      */
230     @Override
231     public NodeData setNodeData(String name, Object value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
232         NodeData nodeData = newNodeDataInstance(name, NodeDataUtil.getJCRPropertyType(value), true);
233         NodeDataUtil.setValue(nodeData, value);
234         return nodeData;
235     }
236 
237     @Override
238     public void deleteNodeData(String name) throws PathNotFoundException, RepositoryException {
239         getNodeData(name).delete();
240     }
241 
242     /**
243      * {@inheritDoc}
244      * Delegates to {@link #getChildren(ItemType)} passing the current node's type.
245      */
246     @Override
247     public Collection<Content> getChildren() {
248         String type = null;
249 
250         try {
251             type = this.getNodeTypeName();
252         }
253         catch (RepositoryException re) {
254             throw new RuntimeException("Can't read type of node [" + toString() + "]", re);
255 
256         }
257         // fix all getChildren calls from the root node
258         if ("rep:root".equalsIgnoreCase(type)) {
259             type = ItemType.CONTENT.getSystemName();
260         }
261         // --------------------------------------------------
262         return this.getChildren(type);
263     }
264 
265     /**
266      * {@inheritDoc}
267      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, java.util.Comparator)}.
268      */
269     @Override
270     public Collection<Content> getChildren(ContentFilter filter) {
271         return getChildren(filter, null);
272     }
273 
274     /**
275      * {@inheritDoc}
276      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, java.util.Comparator)}.
277      */
278     @Override
279     public Collection<Content> getChildren(ItemType itemType) {
280         return getChildren(new NodeTypeFilter(itemType), null);
281     }
282 
283     /**
284      * {@inheritDoc}
285      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, java.util.Comparator)}.
286      */
287     @Override
288     public Collection<Content> getChildren(String contentType) {
289         return getChildren(new NodeTypeFilter(contentType), null);
290     }
291 
292     /**
293      * {@inheritDoc}
294      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, String, java.util.Comparator)}.
295      */
296     @Override
297     public Collection<Content> getChildren(final String contentType, final String namePattern) {
298         return getChildren(new NodeTypeFilter(contentType), namePattern, null);
299     }
300 
301     /**
302      * {@inheritDoc}
303      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, String, java.util.Comparator)}.
304      */
305     @Override
306     public Collection<Content> getChildren(ContentFilter filter, Comparator<Content> orderCriteria) {
307         return getChildren(filter, null, orderCriteria);
308     }
309 
310     /**
311      * @param namePattern ignored if null.
312      */
313     abstract public Collection<Content> getChildren(ContentFilter filter, String namePattern, Comparator<Content> orderCriteria);
314 
315     /**
316      * @deprecated
317      */
318     @Override
319     @Deprecated
320     public Content getChildByName(String namePattern) {
321         Collection<Content> children = getChildren("nt:base", namePattern);
322         if (!children.isEmpty()) {
323             return children.iterator().next();
324         }
325         return null;
326     }
327 
328     @Override
329     public Collection<NodeData> getNodeDataCollection() {
330         return getNodeDataCollection(null);
331     }
332 
333     protected Collection<NodeData> getBinaryNodeDatas(String namePattern) throws RepositoryException {
334         Collection<NodeData> nodeDatas = new ArrayList<NodeData>();
335         Collection<Content> binaryNodes = getChildren(ItemType.NT_RESOURCE, namePattern);
336         for (Content binaryNode : binaryNodes) {
337             nodeDatas.add(newNodeDataInstance(binaryNode.getName(), PropertyType.BINARY, false));
338         }
339         return nodeDatas;
340     }
341 
342 
343     @Override
344     public boolean hasChildren() {
345         return (this.getChildren().size() > 0);
346     }
347 
348     @Override
349     public boolean hasChildren(String contentType) {
350         return (this.getChildren(contentType).size() > 0);
351     }
352 
353     @Override
354     public void delete(String path) throws RepositoryException {
355         if(isNodeData(path)){
356             deleteNodeData(path);
357         }
358         else{
359             getContent(path).delete();
360         }
361     }
362 
363     @Override
364     public boolean isNodeData(String path) throws AccessDeniedException, RepositoryException {
365         return hasNodeData(path);
366     }
367 
368     @Override
369     public String getTemplate() {
370         return this.getMetaData().getTemplate();
371     }
372 
373     @Override
374     public String getTitle() {
375         return I18nContentSupportFactory.getI18nSupport().getNodeData(this, "title").getString();
376     }
377 
378     @Override
379     public void updateMetaData() throws RepositoryException, AccessDeniedException {
380         MetaData md = this.getMetaData();
381         md.setModificationDate();
382         md.setAuthorId(MgnlContext.getUser().getName());
383         AuditLoggingUtil.log(AuditLoggingUtil.ACTION_MODIFY, getWorkspace().getName(), this.getItemType(), getHandle());
384     }
385 
386     @Override
387     public boolean isGranted(long permissions) {
388         // In case getHandle() was manipulated by the subclass (e.g. ContentVersion we need to make sure permissions are checked on the said path)
389         final String handle = getHandle();
390         try {
391             return PermissionUtil.isGranted(this.getJCRNode().getSession(), handle, permissions);
392         } catch (RepositoryException e) {
393             log.error("An error occurred while trying to access path " + handle + " with permissions " + permissions, e);
394             return false;
395         }
396     }
397 
398     @Override
399     public Workspace getWorkspace() throws RepositoryException {
400         return getJCRNode().getSession().getWorkspace();
401     }
402 
403     @Override
404     public String toString() {
405         String type = "";
406         String workspaceName = "";
407 
408         try {
409             workspaceName = getWorkspace() == null ? "null" : getWorkspace().getName();
410             type = getItemType().getSystemName();
411         }
412         catch (RepositoryException e) {
413             // ignore
414         }
415         StringBuilder builder = new StringBuilder();
416         builder.append(workspaceName);
417         builder.append(':').append(getHandle());
418         builder.append('[');
419         builder.append(type);
420         builder.append(']');
421 
422         return builder.toString();
423     }
424 
425 
426 }