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.lang3.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         } catch (RepositoryException e) {
137             throw new IllegalStateException("Can't instantiate node data " + name + " on node " + toString(), e);
138         }
139     }
140 
141     /**
142      * 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.
143      *
144      * @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)
145      */
146     abstract public NodeData newNodeDataInstance(String name, int type, boolean createIfNotExisting) throws AccessDeniedException, RepositoryException;
147 
148     /**
149      * Delegates to {@link NodeData#isExist()}.
150      */
151     @Override
152     public boolean hasNodeData(String name) throws RepositoryException {
153         return getNodeData(name).isExist();
154     }
155 
156     @Override
157     public NodeData setNodeData(String name, Value value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
158         NodeData nodeData = newNodeDataInstance(name, value.getType(), true);
159         nodeData.setValue(value);
160         return nodeData;
161     }
162 
163     @Override
164     public NodeData setNodeData(String name, Value[] value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
165         if (value.length == 0) {
166             throw new IllegalArgumentException("Value array can't be empty");
167         }
168         NodeData nodeData = newNodeDataInstance(name, value[0].getType(), true);
169         nodeData.setValue(value);
170         return nodeData;
171     }
172 
173     @Override
174     public NodeData setNodeData(String name, boolean value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
175         NodeData nodeData = newNodeDataInstance(name, PropertyType.BOOLEAN, true);
176         nodeData.setValue(value);
177         return nodeData;
178     }
179 
180     @Override
181     public NodeData setNodeData(String name, long value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
182         NodeData nodeData = newNodeDataInstance(name, PropertyType.LONG, true);
183         nodeData.setValue(value);
184         return nodeData;
185     }
186 
187     @Override
188     public NodeData setNodeData(String name, double value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
189         NodeData nodeData = newNodeDataInstance(name, PropertyType.DOUBLE, true);
190         nodeData.setValue(value);
191         return nodeData;
192     }
193 
194     @Override
195     public NodeData setNodeData(String name, String value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
196         NodeData nodeData = newNodeDataInstance(name, PropertyType.STRING, true);
197         nodeData.setValue(value);
198         return nodeData;
199     }
200 
201     @Override
202     public NodeData setNodeData(String name, InputStream value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
203         NodeData nodeData = newNodeDataInstance(name, PropertyType.BINARY, true);
204         nodeData.setValue(value);
205         return nodeData;
206     }
207 
208     @Override
209     public NodeData setNodeData(String name, Calendar value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
210         NodeData nodeData = newNodeDataInstance(name, PropertyType.DATE, true);
211         nodeData.setValue(value);
212         return nodeData;
213     }
214 
215     @Override
216     public NodeData setNodeData(String name, Content value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
217         NodeData nodeData = newNodeDataInstance(name, PropertyType.REFERENCE, true);
218         nodeData.setValue(value);
219         return nodeData;
220     }
221 
222     /**
223      * Uses the {@link NodeDataUtil} to create and set the node data based on the object type.
224      */
225     @Override
226     public NodeData setNodeData(String name, Object value) throws PathNotFoundException, RepositoryException, AccessDeniedException {
227         NodeData nodeData = newNodeDataInstance(name, NodeDataUtil.getJCRPropertyType(value), true);
228         NodeDataUtil.setValue(nodeData, value);
229         return nodeData;
230     }
231 
232     @Override
233     public void deleteNodeData(String name) throws PathNotFoundException, RepositoryException {
234         getNodeData(name).delete();
235     }
236 
237     /**
238      * {@inheritDoc}
239      * Delegates to {@link #getChildren(ItemType)} passing the current node's type.
240      */
241     @Override
242     public Collection<Content> getChildren() {
243         String type = null;
244 
245         try {
246             type = this.getNodeTypeName();
247         } catch (RepositoryException re) {
248             throw new RuntimeException("Can't read type of node [" + toString() + "]", re);
249 
250         }
251         // fix all getChildren calls from the root node
252         if ("rep:root".equalsIgnoreCase(type)) {
253             type = ItemType.CONTENT.getSystemName();
254         }
255         // --------------------------------------------------
256         return this.getChildren(type);
257     }
258 
259     /**
260      * {@inheritDoc}
261      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, java.util.Comparator)}.
262      */
263     @Override
264     public Collection<Content> getChildren(ContentFilter filter) {
265         return getChildren(filter, null);
266     }
267 
268     /**
269      * {@inheritDoc}
270      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, java.util.Comparator)}.
271      */
272     @Override
273     public Collection<Content> getChildren(ItemType itemType) {
274         return getChildren(new NodeTypeFilter(itemType), null);
275     }
276 
277     /**
278      * {@inheritDoc}
279      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, java.util.Comparator)}.
280      */
281     @Override
282     public Collection<Content> getChildren(String contentType) {
283         return getChildren(new NodeTypeFilter(contentType), null);
284     }
285 
286     /**
287      * {@inheritDoc}
288      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, String, java.util.Comparator)}.
289      */
290     @Override
291     public Collection<Content> getChildren(final String contentType, final String namePattern) {
292         return getChildren(new NodeTypeFilter(contentType), namePattern, null);
293     }
294 
295     /**
296      * {@inheritDoc}
297      * Delegates to {@link #getChildren(info.magnolia.cms.core.Content.ContentFilter, String, java.util.Comparator)}.
298      */
299     @Override
300     public Collection<Content> getChildren(ContentFilter filter, Comparator<Content> orderCriteria) {
301         return getChildren(filter, null, orderCriteria);
302     }
303 
304     /**
305      * @param namePattern ignored if null.
306      */
307     abstract public Collection<Content> getChildren(ContentFilter filter, String namePattern, Comparator<Content> orderCriteria);
308 
309     /**
310      * @deprecated
311      */
312     @Override
313     @Deprecated
314     public Content getChildByName(String namePattern) {
315         Collection<Content> children = getChildren("nt:base", namePattern);
316         if (!children.isEmpty()) {
317             return children.iterator().next();
318         }
319         return null;
320     }
321 
322     @Override
323     public Collection<NodeData> getNodeDataCollection() {
324         return getNodeDataCollection(null);
325     }
326 
327     protected Collection<NodeData> getBinaryNodeDatas(String namePattern) throws RepositoryException {
328         Collection<NodeData> nodeDatas = new ArrayList<NodeData>();
329         Collection<Content> binaryNodes = getChildren(ItemType.NT_RESOURCE, namePattern);
330         for (Content binaryNode : binaryNodes) {
331             nodeDatas.add(newNodeDataInstance(binaryNode.getName(), PropertyType.BINARY, false));
332         }
333         return nodeDatas;
334     }
335 
336 
337     @Override
338     public boolean hasChildren() {
339         return (this.getChildren().size() > 0);
340     }
341 
342     @Override
343     public boolean hasChildren(String contentType) {
344         return (this.getChildren(contentType).size() > 0);
345     }
346 
347     @Override
348     public void delete(String path) throws RepositoryException {
349         if (isNodeData(path)) {
350             deleteNodeData(path);
351         } else {
352             getContent(path).delete();
353         }
354     }
355 
356     @Override
357     public boolean isNodeData(String path) throws AccessDeniedException, RepositoryException {
358         return hasNodeData(path);
359     }
360 
361     @Override
362     public String getTemplate() {
363         return this.getMetaData().getTemplate();
364     }
365 
366     @Override
367     public String getTitle() {
368         return I18nContentSupportFactory.getI18nSupport().getNodeData(this, "title").getString();
369     }
370 
371     @Override
372     public void updateMetaData() throws RepositoryException, AccessDeniedException {
373         MetaData md = this.getMetaData();
374         md.setModificationDate();
375         md.setAuthorId(MgnlContext.getUser().getName());
376     }
377 
378     @Override
379     public boolean isGranted(long permissions) {
380         // In case getHandle() was manipulated by the subclass (e.g. ContentVersion we need to make sure permissions are checked on the said path)
381         final String handle = getHandle();
382         try {
383             return PermissionUtil.isGranted(this.getJCRNode().getSession(), handle, permissions);
384         } catch (RepositoryException e) {
385             log.error("An error occurred while trying to access path {} with permissions {}", handle, permissions, e);
386             return false;
387         }
388     }
389 
390     @Override
391     public Workspace getWorkspace() throws RepositoryException {
392         return getJCRNode().getSession().getWorkspace();
393     }
394 
395     @Override
396     public String toString() {
397         String type = "";
398         String workspaceName = "";
399 
400         try {
401             workspaceName = getWorkspace() == null ? "null" : getWorkspace().getName();
402             type = getItemType().getSystemName();
403         } catch (RepositoryException e) {
404             // ignore
405         }
406         StringBuilder builder = new StringBuilder();
407         builder.append(workspaceName);
408         builder.append(':').append(getHandle());
409         builder.append('[');
410         builder.append(type);
411         builder.append(']');
412 
413         return builder.toString();
414     }
415 
416 
417 }