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