View Javadoc

1   /**
2    * This file Copyright (c) 2008-2013 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.module.templatingkit.templates.category;
35  
36  import info.magnolia.cms.core.Content;
37  import info.magnolia.cms.core.MgnlNodeType;
38  import info.magnolia.cms.core.search.Query;
39  import info.magnolia.cms.util.ContentUtil;
40  import info.magnolia.cms.util.QueryUtil;
41  import info.magnolia.jcr.wrapper.HTMLEscapingNodeWrapper;
42  import info.magnolia.jcr.wrapper.I18nNodeWrapper;
43  import info.magnolia.module.templatingkit.templates.pages.STKPage;
44  import info.magnolia.objectfactory.Components;
45  import info.magnolia.registry.RegistrationException;
46  import info.magnolia.rendering.template.TemplateDefinition;
47  import info.magnolia.rendering.template.registry.TemplateDefinitionRegistry;
48  
49  import java.util.ArrayList;
50  import java.util.HashSet;
51  import java.util.Iterator;
52  import java.util.List;
53  import java.util.Set;
54  
55  import javax.jcr.LoginException;
56  import javax.jcr.Node;
57  import javax.jcr.NodeIterator;
58  import javax.jcr.RepositoryException;
59  import javax.jcr.Session;
60  
61  import org.apache.commons.lang.StringUtils;
62  import org.slf4j.Logger;
63  import org.slf4j.LoggerFactory;
64  
65  /**
66   * Few util methods to use template categories.
67   * 
68   * @author pbracher
69   * @version $Id$
70   */
71  public class TemplateCategoryUtil {
72  
73      private static Logger log = LoggerFactory.getLogger(TemplateCategoryUtil.class);
74  
75      /**
76       * FIXME nested method call: for work around {@link Content}.
77       */
78      public static String getTemplateCategory(Node pageNode) {
79          return getTemplateCategory(ContentUtil.asContent(pageNode));
80      }
81  
82      /**
83       * Returns the category of the template assigned to a node, if the assigned template is not an STK template it
84       * default to {@link TemplateCategory#CONTENT} and if there is no template assigned or the assigned template doesn't
85       * exists it returns the empty string.
86       */
87      public static String getTemplateCategory(Content pageNode) {
88          final String templateId = pageNode.getTemplate();
89          try {
90              TemplateDefinition template = Components.getComponent(TemplateDefinitionRegistry.class).getTemplateDefinition(templateId);
91              if (template instanceof STKPage) {
92                  STKPage stkTemplate = (STKPage) template;
93                  return stkTemplate.getCategory();
94              }
95              return TemplateCategory.CONTENT;
96          } catch (RegistrationException e) {
97              log.debug(e.getMessage(), e);
98              return "";
99          }
100     }
101 
102     /**
103      * FIXME nested method call: for work around {@link Content}.
104      */
105     public static String getTemplateSubCategory(Node pageNode) {
106         return getTemplateSubCategory(ContentUtil.asContent(pageNode));
107     }
108 
109     /**
110      * Returns the sub-category of the template assigned to a node, if the assigned template is not an STK template it
111      * default to {@link TemplateCategory#CONTENT} and if there is no template assigned or the assigned template doesn't
112      * exists it returns the empty string.
113      */
114     public static String getTemplateSubCategory(Content pageNode) {
115         final String templateId = pageNode.getTemplate();
116         if (StringUtils.isBlank(templateId)) {
117             log.debug("Page [" + pageNode.getHandle() + "] doesn't have assigned any template.");
118             return "";
119         }
120         try {
121             TemplateDefinition template = Components.getComponent(TemplateDefinitionRegistry.class).getTemplateDefinition(templateId);
122             if (template instanceof STKPage) {
123                 STKPage stkTemplate = (STKPage) template;
124                 return stkTemplate.getSubcategory();
125             }
126             return TemplateCategory.CONTENT;
127         } catch (RegistrationException e) {
128             return "";
129         }
130     }
131 
132     /**
133      * FIXME nested method call: for work around {@link Content}.
134      */
135     public static boolean hasTemplateOfCategory(Node pageNode, String templateCatergory) {
136         return hasTemplateOfCategory(ContentUtil.asContent(pageNode), templateCatergory);
137     }
138 
139     public static boolean hasTemplateOfCategory(Content pageNode, String templateCatergory) {
140         return TemplateCategoryUtil.getTemplateCategory(pageNode).equals(templateCatergory);
141     }
142 
143     /**
144      * FIXME nested method call: for work around {@link Content}.
145      */
146     public static Node findParentWithTemplateCategory(Node pageNode, String templateCategory) throws RepositoryException {
147         Content parent = findParentWithTemplateCategory(ContentUtil.asContent(pageNode), templateCategory);
148         return parent != null ? parent.getJCRNode() : null;
149     }
150 
151     public static Content findParentWithTemplateCategory(Content pageNode, String templateCategory) throws RepositoryException {
152         Content current = pageNode;
153         // in a multi site environment the root might be the home page
154         while (current.getLevel() >= 0) {
155             if (TemplateCategoryUtil.hasTemplateOfCategory(current, templateCategory)) {
156                 return current;
157             }
158             // avoid javax.jcr.ItemNotFoundException: root node doesn't have a parent
159             if (current.getLevel() == 0) {
160                 break;
161             }
162             current = current.getParent();
163         }
164         return null;
165     }
166 
167     /**
168      * FIXME nested method call: for work around {@link Content}.
169      */
170     public static boolean isContentPage(Node pageNode) {
171         return isContentPage(ContentUtil.asContent(pageNode));
172     }
173 
174     public static boolean isContentPage(Content pageNode) {
175         return !TemplateCategoryUtil.hasTemplateOfCategory(pageNode, TemplateCategory.HOME) && !!TemplateCategoryUtil.hasTemplateOfCategory(pageNode, TemplateCategory.SECTION);
176     }
177 
178     public static List<Node> getContentListByTemplateCategorySubCategory(Node siteRoot, String category, String subCategory) throws RepositoryException {
179         return getContentListByTemplateCategorySubCategory(siteRoot, category, subCategory, Integer.MAX_VALUE, null, null);
180     }
181 
182     public static List<Node> getContentListByTemplateCategorySubCategory(Node siteRoot, String category, String subCategory, int maxResultSize, String andClause, String orderBy) throws RepositoryException {
183         final Set<String> templateIds = new HashSet<String>();
184         final Iterator<TemplateDefinition> templatesIterator = Components.getComponent(TemplateDefinitionRegistry.class).getTemplateDefinitions().iterator();
185         while (templatesIterator.hasNext()) {
186             final TemplateDefinition template = templatesIterator.next();
187             if (template instanceof STKPage) {
188                 final STKPage stkTemplate = (STKPage) template;
189                 if (StringUtils.equals(stkTemplate.getCategory(), category) // TODO: the second condition fails
190                         && (StringUtils.isEmpty(subCategory) || StringUtils.equals(stkTemplate.getSubcategory(), subCategory))) {
191                     templateIds.add(template.getId());
192                 }
193             }
194         }
195         if (!templateIds.isEmpty()) {
196             return getContentListByTemplateNames(siteRoot, templateIds, maxResultSize, andClause, orderBy);
197         } else {
198             return new ArrayList<Node>();
199         }
200     }
201 
202     public static List<Node> getContentListByTemplateName(Node searchRoot, String templateName) throws RepositoryException {
203         return getContentListByTemplateName(searchRoot, templateName, Integer.MAX_VALUE, null, null);
204     }
205 
206     public static List<Node> getContentListByTemplateName(Node searchRoot, String templateName, int maxResultSize, String andClause, String orderByClause) throws RepositoryException {
207         Set<String> templateNames = new HashSet<String>();
208         templateNames.add(templateName);
209         return getContentListByTemplateNames(searchRoot, templateNames, maxResultSize, andClause, orderByClause);
210     }
211 
212     /**
213      * Find content objects with one of the given templates below a given search root.
214      * 
215      * @param searchRoot
216      * @param templateIds
217      * @param maxResultSize setting this can drastically improve query performance, if you are interested only in a fixed number of leading result objects
218      * @param andClause an additional "AND" clause in SQL syntax, excluding the "AND" itself, e.g. "date IS NOT NULL"
219      * @param orderByClause an "ORDER BY" clause in SQL syntax, excluding the "ORDER BY" itself, e.g. "date desc" or "date asc"
220      * @return
221      * @throws RepositoryException
222      */
223     public static List<Node> getContentListByTemplateNames(Node searchRoot, Set<String> templateIds, int maxResultSize, String andClause, String orderByClause) throws RepositoryException {
224         if (log.isDebugEnabled()) {
225             log.debug("Node: [" + searchRoot.getPath() + "]; Template IDs: [" + StringUtils.join(templateIds, ", ") + "]; MaxResultSize: [" + maxResultSize + "]; AndClause: [" + andClause + "]; OrderByClause: [" + orderByClause + "]");
226         }
227         Session session = searchRoot.getSession();
228         String path = searchRoot.getPath();
229         String repository = session.getWorkspace().getName();
230 
231         StringBuffer sql = new StringBuffer("select * from nt:base where jcr:path like '" + path + "/%'");
232         if (templateIds != null && !templateIds.isEmpty()) {
233             sql.append(" AND (");
234             Iterator<String> templateNamesIter = templateIds.iterator();
235             do {
236                 sql.append("mgnl:template = '" + templateNamesIter.next() + "'");
237                 if (templateNamesIter.hasNext()) {
238                     sql.append(" OR ");
239                 }
240             } while (templateNamesIter.hasNext());
241             sql.append(")");
242         }
243         if (andClause != null) {
244             sql.append(" AND " + andClause);
245         }
246         if (orderByClause != null) {
247             sql.append(" ORDER BY " + orderByClause);
248         }
249 
250         return getWrappedNodesFromQuery(sql.toString(), repository, maxResultSize);
251     }
252 
253     private static List<Node> getWrappedNodesFromQuery(String sql, String repository, long maxResultSize) throws LoginException, RepositoryException {
254         List<Node> itemsListFromQuery = new ArrayList<Node>();
255         log.debug("SQL query: [" + sql + "]");
256         NodeIterator items = QueryUtil.search(repository, sql.toString(), Query.SQL, MgnlNodeType.NT_CONTENT);
257         log.debug("SQL query done, now will wrap all items with wrappers HTMLEscaping and i18n wrappers");
258         long count = 1;
259         while (items.hasNext() && count <= maxResultSize) {
260             itemsListFromQuery.add(new HTMLEscapingNodeWrapper(new I18nNodeWrapper(items.nextNode()), false));
261             count++;
262         }
263         log.debug("Wrapped " + count + "items.");
264         return itemsListFromQuery;
265     }
266 
267     public static Node getNearestContentByTemplateCategorySubCategory(Node siteRoot, String category, String subCategory, Node current) throws RepositoryException {
268 
269         List<Node> nodeList = getContentListByTemplateCategorySubCategory(siteRoot, category, subCategory);
270         if (!nodeList.isEmpty()) {
271             Node bestMatching = null;
272             int bestMatchingLength = -1;
273             for (Node node : nodeList) {
274                 int matchingLegth = StringUtils.indexOfDifference(node.getPath(), current.getPath());
275                 if (matchingLegth > bestMatchingLength) {
276                     bestMatchingLength = matchingLegth;
277                     bestMatching = node;
278                 }
279             }
280             return bestMatching;
281         }
282         return null;
283     }
284 }