View Javadoc
1   /**
2    * This file Copyright (c) 2008-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.rendering.template.type;
35  
36  import info.magnolia.cms.util.QueryUtil;
37  import info.magnolia.jcr.util.NodeTypes;
38  import info.magnolia.jcr.wrapper.HTMLEscapingNodeWrapper;
39  import info.magnolia.jcr.wrapper.I18nNodeWrapper;
40  import info.magnolia.registry.RegistrationException;
41  import info.magnolia.rendering.template.TemplateDefinition;
42  import info.magnolia.rendering.template.registry.TemplateDefinitionRegistry;
43  
44  import java.util.ArrayList;
45  import java.util.Collections;
46  import java.util.HashSet;
47  import java.util.Iterator;
48  import java.util.List;
49  import java.util.Set;
50  
51  import javax.inject.Inject;
52  import javax.inject.Singleton;
53  import javax.jcr.Node;
54  import javax.jcr.NodeIterator;
55  import javax.jcr.RepositoryException;
56  import javax.jcr.Session;
57  import javax.jcr.query.Query;
58  
59  import org.apache.commons.lang3.StringUtils;
60  import org.slf4j.Logger;
61  import org.slf4j.LoggerFactory;
62  
63  /**
64   * Helper functions for {@link DefaultTemplateTypes}, i.e. template types set for a
65   * {@link info.magnolia.rendering.template.TemplateDefinition} (see:
66   * {@link info.magnolia.rendering.template.TemplateDefinition#getType()} and
67   * {@link info.magnolia.rendering.template.TemplateDefinition#getSubtype()}).
68   *
69   * @see info.magnolia.rendering.template.TemplateDefinition
70   * @see info.magnolia.rendering.template.type.DefaultTemplateTypes
71   */
72  @Singleton
73  public class TemplateTypeHelper {
74  
75      private static final Logger log = LoggerFactory.getLogger(TemplateTypeHelper.class);
76  
77      private final TemplateDefinitionRegistry templateDefinitionRegistry;
78  
79      @Inject
80      public TemplateTypeHelper(TemplateDefinitionRegistry templateDefinitionRegistry) {
81          this.templateDefinitionRegistry = templateDefinitionRegistry;
82      }
83  
84      /**
85       * Returns the type of the template assigned to a node, if the assigned template is not a
86       * {@link info.magnolia.rendering.template.TemplateDefinition} it defaults to
87       * {@link DefaultTemplateTypes#CONTENT} and if there is no template assigned
88       * or the assigned template doesn't exists it returns the empty string.
89       */
90      public String getTemplateTypeOrDefault(Node pageNode) {
91          final TemplateDefinition template = getTemplateDefinition(pageNode);
92          if (template == null) {
93              return "";
94          }
95  
96          final String templateType = template.getType();
97          if (templateType != null) {
98              return templateType;
99          }
100 
101         return DefaultTemplateTypes.CONTENT;
102     }
103 
104     /**
105      * Returns the subtype of the template assigned to a node, if the assigned template is not a
106      * {@link info.magnolia.rendering.template.TemplateDefinition} it defaults to
107      * {@link DefaultTemplateTypes#CONTENT} and if there is no template assigned
108      * or the assigned template doesn't exists it returns the empty string.
109      */
110     public String getTemplateSubtypeOrDefault(Node pageNode) {
111         final TemplateDefinition template = getTemplateDefinition(pageNode);
112         if (template == null) {
113             return "";
114         }
115 
116         final String templateSubtype = template.getSubtype();
117         if (templateSubtype != null) {
118             return templateSubtype;
119         }
120 
121         return DefaultTemplateTypes.CONTENT;
122     }
123 
124     /**
125      * Tries to find the {@link info.magnolia.rendering.template.TemplateDefinition} of given page {@link javax.jcr.Node}
126      * in the {@link info.magnolia.rendering.template.registry.TemplateDefinitionRegistry}.
127      *
128      * Returns <code>null</code> when no template is set or given template cannot be found in the
129      * {@link info.magnolia.rendering.template.registry.TemplateDefinitionRegistry}.
130      */
131     private TemplateDefinition getTemplateDefinition(Node pageNode) {
132         String templateId = null;
133 
134         try {
135             templateId = NodeTypes.Renderable.getTemplate(pageNode);
136             if (StringUtils.isBlank(templateId)) {
137                 log.warn("Page '{}' doesn't have any template assigned.", pageNode);
138                 return null;
139             }
140 
141             return templateDefinitionRegistry.getTemplateDefinition(templateId);
142         } catch (RepositoryException re) {
143             log.warn("Could not get path or template for node '{}'.", pageNode, re);
144         } catch (RegistrationException re) {
145             log.warn("Could not get the template definition for node '{}' and templateId '{}'.", pageNode, templateId, re);
146         }
147 
148         return null;
149     }
150 
151     /**
152      * Checks whether the given page-{@link javax.jcr.Node} has the specified <code>templateType</code>.
153      */
154     public boolean hasTemplateOfType(Node pageNode, String templateType) {
155         return templateType != null && templateType.equals(getTemplateTypeOrDefault(pageNode));
156     }
157 
158     /**
159      * Finds a parent {@link javax.jcr.Node} of given {@link javax.jcr.Node} with the specified <code>templateType</code>.
160      */
161     public Node findParentWithTemplateType(Node pageNode, String templateType) throws RepositoryException {
162         Node current = pageNode;
163 
164         // In a multi site environment the root might be the home page
165         while (current.getDepth() >= 0) {
166             if (hasTemplateOfType(current, templateType)) {
167                 return current;
168             }
169             // Avoid javax.jcr.ItemNotFoundException: root node doesn't have a parent
170             if (current.getDepth() == 0) {
171                 break;
172             }
173             current = current.getParent();
174         }
175 
176         return null;
177     }
178 
179     /**
180      * Returns a {@link java.util.Set} of templateIds (see {@link info.magnolia.rendering.template.TemplateDefinition#getId()})
181      * by comparing their template type and subtype.
182      *
183      * TODO: replace with query functionality of registries once available
184      *
185      * @deprecated since 5.4 - use upcoming query functionality of registries once implemented.
186      */
187     @Deprecated
188     private Set<String> getTemplateIdsByTemplateType(final String templateType, final String templateSubtype) {
189         final Set<String> templateIds = new HashSet<String>();
190         final Iterator<TemplateDefinition> templatesIterator = templateDefinitionRegistry.getTemplateDefinitions().iterator();
191 
192         while (templatesIterator.hasNext()) {
193             final TemplateDefinition template = templatesIterator.next();
194 
195             final String thatTemplateType = template.getType();
196             final String thatTemplateSubtype = template.getSubtype();
197 
198             if (templateType != null && templateType.equals(thatTemplateType) && (templateSubtype == null || templateSubtype.equals(thatTemplateSubtype))) {
199                 templateIds.add(template.getId());
200             }
201         }
202 
203         return templateIds;
204     }
205 
206     /**
207      * Find content objects with one of the given template (sub) types below given search root.
208      */
209     public List<Node> getContentListByTemplateType(Node searchRoot, String templateType, String templateSubtype, int maxResultSize, String andClause, String orderByClause) throws RepositoryException {
210         final Set<String> templateIds = getTemplateIdsByTemplateType(templateType, templateSubtype);
211 
212         if (!templateIds.isEmpty()) {
213             return getContentListByTemplateIds(searchRoot, templateIds, maxResultSize, andClause, orderByClause);
214         } else {
215             return Collections.emptyList();
216         }
217     }
218 
219     /**
220      * Find content objects with one of the given templates below a given search root.
221      *
222      * @param searchRoot the {@link javax.jcr.Node} to use as root of the search
223      * @param templateIds a {@link java.util.Set} of template IDs to search for
224      * @param maxResultSize setting this can drastically improve query performance, if you are interested only in a fixed number of leading result objects
225      * @param andClause an additional "AND" clause in SQL syntax, excluding the "AND" itself, e.g. "date IS NOT NULL"
226      * @param orderByClause an "ORDER BY" clause in SQL syntax, excluding the "ORDER BY" itself, e.g. "date desc" or "date asc"
227      */
228     public List<Node> getContentListByTemplateIds(Node searchRoot, Set<String> templateIds, int maxResultSize, String andClause, String orderByClause) throws RepositoryException {
229         log.debug("Node: [{}]; Template IDs: [{}]; MaxResultSize: [{}]; AndClause: [{}]; OrderByClause: [{}]", searchRoot, templateIds, maxResultSize, andClause, orderByClause);
230 
231         final Session session = searchRoot.getSession();
232         final String repository = session.getWorkspace().getName();
233         String path = searchRoot.getPath();
234         if ("/".equals(path)) {
235             path = "";
236         }
237 
238         final StringBuilder sql = new StringBuilder("SELECT * FROM nt:base WHERE jcr:path LIKE '").append(path).append("/%'");
239         if (templateIds != null && !templateIds.isEmpty()) {
240             sql.append(" AND (");
241             final Iterator<String> templateNamesIterator = templateIds.iterator();
242             do {
243                 sql.append("mgnl:template='").append(templateNamesIterator.next()).append("'");
244                 if (templateNamesIterator.hasNext()) {
245                     sql.append(" OR ");
246                 }
247             } while (templateNamesIterator.hasNext());
248             sql.append(")");
249         }
250         if (andClause != null) {
251             sql.append(" AND ").append(andClause);
252         }
253         if (orderByClause != null) {
254             sql.append(" ORDER BY ").append(orderByClause);
255         }
256 
257         return getWrappedNodesFromQuery(sql.toString(), repository, maxResultSize);
258     }
259 
260     /**
261      * Executes {@link javax.jcr.query.Query#SQL} {@link javax.jcr.query.Query} and wraps {@link javax.jcr.Node}s
262      * in {@link info.magnolia.jcr.wrapper.HTMLEscapingNodeWrapper} and {@link info.magnolia.jcr.wrapper.I18nNodeWrapper}
263      * before returning.
264      */
265     private List<Node> getWrappedNodesFromQuery(String sql, String repository, long maxResultSize) throws RepositoryException {
266         final List<Node> itemsListFromQuery = new ArrayList<Node>();
267         log.debug("SQL query: [{}]", sql);
268         final NodeIterator items = QueryUtil.search(repository, sql, Query.SQL, NodeTypes.Content.NAME);
269         log.debug("SQL query done, now will wrap all items with wrappers HTMLEscaping and i18n wrappers");
270         long count = 1;
271         while (items.hasNext() && count <= maxResultSize) {
272             itemsListFromQuery.add(new HTMLEscapingNodeWrapper(new I18nNodeWrapper(items.nextNode()), false));
273             count++;
274         }
275         log.debug("Wrapped {} items.", count);
276         return itemsListFromQuery;
277     }
278 
279 }