View Javadoc
1   /**
2    * This file Copyright (c) 2008-2018 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             current = current.getParent();
170         }
171 
172         return null;
173     }
174 
175     /**
176      * Returns a {@link java.util.Set} of templateIds (see {@link info.magnolia.rendering.template.TemplateDefinition#getId()})
177      * by comparing their template type and subtype.
178      *
179      * TODO: replace with query functionality of registries once available
180      *
181      * @deprecated since 5.4 - use upcoming query functionality of registries once implemented.
182      */
183     @Deprecated
184     private Set<String> getTemplateIdsByTemplateType(final String templateType, final String templateSubtype) {
185         final Set<String> templateIds = new HashSet<String>();
186         final Iterator<TemplateDefinition> templatesIterator = templateDefinitionRegistry.getTemplateDefinitions().iterator();
187 
188         while (templatesIterator.hasNext()) {
189             final TemplateDefinition template = templatesIterator.next();
190 
191             final String thatTemplateType = template.getType();
192             final String thatTemplateSubtype = template.getSubtype();
193 
194             if (templateType != null && templateType.equals(thatTemplateType) && (templateSubtype == null || templateSubtype.equals(thatTemplateSubtype))) {
195                 templateIds.add(template.getId());
196             }
197         }
198 
199         return templateIds;
200     }
201 
202     /**
203      * Find content objects with one of the given template (sub) types below given search root.
204      */
205     public List<Node> getContentListByTemplateType(Node searchRoot, String templateType, String templateSubtype, int maxResultSize, String andClause, String orderByClause) throws RepositoryException {
206         final Set<String> templateIds = getTemplateIdsByTemplateType(templateType, templateSubtype);
207 
208         if (!templateIds.isEmpty()) {
209             return getContentListByTemplateIds(searchRoot, templateIds, maxResultSize, andClause, orderByClause);
210         } else {
211             return Collections.emptyList();
212         }
213     }
214 
215     /**
216      * Find content objects with one of the given templates below a given search root.
217      *
218      * @param searchRoot the {@link javax.jcr.Node} to use as root of the search
219      * @param templateIds a {@link java.util.Set} of template IDs to search for
220      * @param maxResultSize setting this can drastically improve query performance, if you are interested only in a fixed number of leading result objects
221      * @param andClause an additional "AND" clause in SQL syntax, excluding the "AND" itself, e.g. "date IS NOT NULL"
222      * @param orderByClause an "ORDER BY" clause in SQL syntax, excluding the "ORDER BY" itself, e.g. "date desc" or "date asc"
223      */
224     public List<Node> getContentListByTemplateIds(Node searchRoot, Set<String> templateIds, int maxResultSize, String andClause, String orderByClause) throws RepositoryException {
225         log.debug("Node: [{}]; Template IDs: [{}]; MaxResultSize: [{}]; AndClause: [{}]; OrderByClause: [{}]", searchRoot, templateIds, maxResultSize, andClause, orderByClause);
226 
227         final Session session = searchRoot.getSession();
228         final String repository = session.getWorkspace().getName();
229         String path = searchRoot.getPath();
230         if ("/".equals(path)) {
231             path = "";
232         }
233 
234         final StringBuilder sql = new StringBuilder("SELECT * FROM nt:base WHERE jcr:path LIKE '").append(path).append("/%'");
235         if (templateIds != null && !templateIds.isEmpty()) {
236             sql.append(" AND (");
237             final Iterator<String> templateNamesIterator = templateIds.iterator();
238             do {
239                 sql.append("mgnl:template='").append(templateNamesIterator.next()).append("'");
240                 if (templateNamesIterator.hasNext()) {
241                     sql.append(" OR ");
242                 }
243             } while (templateNamesIterator.hasNext());
244             sql.append(")");
245         }
246         if (andClause != null) {
247             sql.append(" AND ").append(andClause);
248         }
249         if (orderByClause != null) {
250             sql.append(" ORDER BY ").append(orderByClause);
251         }
252 
253         return getWrappedNodesFromQuery(sql.toString(), repository, maxResultSize);
254     }
255 
256     /**
257      * Executes {@link javax.jcr.query.Query#SQL} {@link javax.jcr.query.Query} and wraps {@link javax.jcr.Node}s
258      * in {@link info.magnolia.jcr.wrapper.HTMLEscapingNodeWrapper} and {@link info.magnolia.jcr.wrapper.I18nNodeWrapper}
259      * before returning.
260      */
261     private List<Node> getWrappedNodesFromQuery(String sql, String repository, long maxResultSize) throws RepositoryException {
262         final List<Node> itemsListFromQuery = new ArrayList<Node>();
263         log.debug("SQL query: [{}]", sql);
264         final NodeIterator items = QueryUtil.search(repository, sql, Query.SQL, NodeTypes.Content.NAME);
265         log.debug("SQL query done, now will wrap all items with wrappers HTMLEscaping and i18n wrappers");
266         long count = 1;
267         while (items.hasNext() && count <= maxResultSize) {
268             itemsListFromQuery.add(new HTMLEscapingNodeWrapper(new I18nNodeWrapper(items.nextNode()), false));
269             count++;
270         }
271         log.debug("Wrapped {} items.", count);
272         return itemsListFromQuery;
273     }
274 
275 }