Clover Coverage Report - Google Sitemap Module 2.0.3
Coverage timestamp: Fri Mar 14 2014 11:34:22 CET
../../../../../img/srcFileCovDistChart9.png 2% of files have more coverage
134   452   60   9.57
72   251   0.45   14
14     4.29  
1    
 
  SiteMapService       Line # 86 134 0% 60 28 87.3% 0.8727273
 
No Tests
 
1    /**
2    * This file Copyright (c) 2012 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.googlesitemap.service;
35   
36    import info.magnolia.cms.i18n.I18nContentSupport;
37    import info.magnolia.context.MgnlContext;
38    import info.magnolia.jcr.predicate.AbstractPredicate;
39    import info.magnolia.jcr.util.NodeTypes;
40    import info.magnolia.jcr.util.NodeUtil;
41    import info.magnolia.jcr.util.SessionUtil;
42    import info.magnolia.jcr.wrapper.JCRMgnlPropertiesFilteringNodeWrapper;
43    import info.magnolia.link.LinkUtil;
44    import info.magnolia.module.googlesitemap.GoogleSiteMapConfiguration;
45    import info.magnolia.module.googlesitemap.bean.SiteMapEntry;
46    import info.magnolia.module.googlesitemap.service.query.QueryUtil;
47    import info.magnolia.module.templatingkit.sites.Site;
48    import info.magnolia.module.templatingkit.sites.SiteManager;
49    import info.magnolia.objectfactory.Components;
50    import info.magnolia.repository.RepositoryConstants;
51   
52    import java.util.ArrayList;
53    import java.util.Calendar;
54    import java.util.HashSet;
55    import java.util.Iterator;
56    import java.util.List;
57    import java.util.Locale;
58    import java.util.Set;
59   
60    import javax.inject.Inject;
61    import javax.inject.Singleton;
62    import javax.jcr.Node;
63    import javax.jcr.NodeIterator;
64    import javax.jcr.Property;
65    import javax.jcr.PropertyIterator;
66    import javax.jcr.RepositoryException;
67   
68    import org.apache.commons.lang.StringUtils;
69    import org.apache.jackrabbit.commons.iterator.FilteringNodeIterator;
70    import org.apache.jackrabbit.commons.predicate.NodeTypePredicate;
71    import org.apache.jackrabbit.commons.predicate.Predicate;
72    import org.slf4j.Logger;
73    import org.slf4j.LoggerFactory;
74   
75   
76    /**
77    * Main SiteMapService.
78    * This service is responsible for:
79    * Searching the appropriate nodes (pages or VirtualUri) that should be displayed (for XML rendering or Editing).
80    * From the search nodes, create SiteMapEntrys (POJO containing preformated infos used for the rendering).
81    *
82    * @version $Id$
83    *
84    */
85    @Singleton
 
86    public class SiteMapService {
87   
88    private static final Logger log = LoggerFactory.getLogger(SiteMapService.class);
89   
90    public static final double DEFAULT_PRIORITY = 0.5;
91    public static final String DEFAULT_CHANGE_FREQUENCY = "weekly";
92   
93    // Used to filter the current node and just get child nodes starting with SITE_DIALOG_CONFIGURATION_NAME
94    private static Predicate All_Site_Nodes = new AbstractPredicate<Node>() {
 
95  10 toggle @Override
96    public boolean evaluateTyped(Node node) {
97  10 try {
98  10 return node.getName().startsWith(GoogleSiteMapConfiguration.SITE_DIALOG_CONFIGURATION_NAME);
99    } catch (RepositoryException e) {
100  0 return false;
101    }
102    }
103    };
104   
105    /**
106    * Injected service.
107    */
108    private SiteManager siteManager;
109    private I18nContentSupport i18nSupport;
110    private GoogleSiteMapConfiguration configuration;
111    private QueryUtil queryUtil;
112   
113   
114    /**
115    * Constructor for injection.
116    * @param siteManager: Injected.
117    */
 
118  20 toggle @Inject
119    public SiteMapService(SiteManager siteManager, GoogleSiteMapConfiguration configuration, QueryUtil queryUtil) {
120  20 this.siteManager = siteManager;
121  20 this.configuration = configuration;
122  20 this.queryUtil = queryUtil;
123  20 i18nSupport = Components.getComponent(I18nContentSupport.class);
124    }
125   
126   
127    /**
128    * Create the SiteMapEntry List corresponding to the uriMapping.
129    */
 
130  7 toggle public List<SiteMapEntry> getSiteMapBeanForVirtualUri( boolean isForEdit) throws RepositoryException{
131    // Init
132  7 List<SiteMapEntry> res = new ArrayList<SiteMapEntry>();
133  7 NodeIterator nodeIterator = null;
134   
135  7 log.info("Requested virtualUri info's for EDIT ");
136    // Create a virtualUri info beans
137  7 nodeIterator = searchVirtualUriNodes();
138  7 feedVirtualUriMapBeans(res, nodeIterator, isForEdit);
139   
140  7 return res;
141    }
142   
143    /**
144    * Create the SiteMapEntry for all child's of the rootNode that are of type MgnlNodeType.NT_CONTENT.
145    */
 
146  19 toggle public List<SiteMapEntry> getSiteMapBeanForSite(Node rootNode, boolean isForEdit) throws RepositoryException{
147    // Init
148  19 List<SiteMapEntry> res = new ArrayList<SiteMapEntry>();
149  19 NodeIterator nodeIterator = null;
150   
151  19 log.info("Requested siteMap info's for EDIT based on the following root: "+rootNode.getPath());
152    // Create a site info beans
153  19 nodeIterator = searchSiteChildNodes(rootNode);
154  19 feedSiteMapBeans(res, nodeIterator, isForEdit, null);
155   
156  19 return res;
157    }
158   
159    /**
160    * Search the nodes related to the UriMappings.
161    */
 
162  7 toggle private NodeIterator searchVirtualUriNodes() {
163  7 NodeIterator nodes = null;
164   
165  7 String xpath = "//virtualURIMapping//element(*," + NodeTypes.ContentNode.NAME + ")";
166  7 nodes = queryUtil.query(RepositoryConstants.CONFIG, xpath, "xpath", NodeTypes.ContentNode.NAME);
167   
168  7 return nodes;
169    }
170   
171   
172    /**
173    * Extract informations form the Node and create a Bean used in the display
174    * for Edit and XML rendering.
175    */
 
176  7 toggle private void feedVirtualUriMapBeans(List<SiteMapEntry> siteMapBeans, NodeIterator nodes, boolean isForEdit) throws RepositoryException {
177  21 while(nodes.hasNext()) {
178    // Init
179  14 Node child = nodes.nextNode();
180  14 if(hasToBePopulated(child, true, isForEdit, null)) {
181    //Populate the Bean used by the display
182  9 SiteMapEntry siteMapBean = populateBean(child, true, isForEdit , null, null);
183    //Add the bean to the result
184  9 siteMapBeans.add(siteMapBean);
185    }
186    }
187    }
188   
189   
190    /**
191    * Search children of the root Node of type MgnlNodeType.NT_CONTENT and all inherited node types.
192    * @throws RepositoryException
193    */
 
194  43 toggle private NodeIterator searchSiteChildNodes(Node root) throws RepositoryException {
195  43 NodeIterator nodes = null;
196   
197  43 NodeTypePredicate predicate = new NodeTypePredicate(NodeTypes.Content.NAME, true);
198  43 nodes = new FilteringNodeIterator(root.getNodes(), predicate);
199   
200  43 return nodes;
201    }
202   
203   
204    /**
205    * Create a new SiteMapEntry POJO for all node from the NodeIterator if required.
206    */
 
207  26 toggle private void feedSiteMapBeans(List<SiteMapEntry> siteMapBeans, NodeIterator nodes, boolean isForEdit, Boolean inheritedParentDisplayColor) throws RepositoryException {
208   
209  61 while(nodes.hasNext()) {
210    // Init
211  35 Node child = nodes.nextNode();
212  35 Site site = siteManager.getAssignedSite(child);
213   
214  35 if(hasToBePopulated(child, false, isForEdit, site)) {
215    //Populate the Bean used by the display
216  24 SiteMapEntry siteMapBean = populateBean(child, false, isForEdit , site, inheritedParentDisplayColor);
217  24 NodeIterator childNodes = searchSiteChildNodes(child);
218  24 boolean hasChild = (childNodes!=null && childNodes.hasNext()) ? true:false;
219   
220  24 if(isForEdit) {
221    // Handle Edit Mode. Always all the bean.
222  6 siteMapBeans.add(siteMapBean);
223   
224  6 if(hasChild) {
225    // Handle the children if any
226  3 Boolean hideNodeChildInGoogleSitemapTmp = null;
227    // Handle the color of the child to be display.
228  3 if(inheritedParentDisplayColor != null) {
229  0 hideNodeChildInGoogleSitemapTmp = inheritedParentDisplayColor;
230  3 }else if(hideNodeChildInGoogleSitemap(child)) {
231  1 hideNodeChildInGoogleSitemapTmp = Boolean.TRUE;
232    }
233   
234  3 feedSiteMapBeans(siteMapBeans, childNodes, isForEdit, hideNodeChildInGoogleSitemapTmp);
235    }
236   
237    } else {
238    // Handle XML Display Mode
239    // If node has to be display.
240  18 if (!hideNodeInGoogleSitemap(child)){
241    // Check Multilang.
242  17 if(i18nSupport.isEnabled() && site.getI18n().isEnabled()) {
243  0 Locale currentLocale = i18nSupport.getLocale();
244  0 for(Locale locale:site.getI18n().getLocales() ){
245  0 i18nSupport.setLocale(locale);
246  0 SiteMapEntry siteMapBeanLocale = populateBean(child, false, isForEdit, site, null);
247  0 if(siteMapBeanLocale != null) {
248  0 siteMapBeans.add(siteMapBeanLocale);
249    }
250    }
251  0 i18nSupport.setLocale(currentLocale);
252    } else {
253  17 siteMapBeans.add(siteMapBean);
254    }
255    }
256    // Check if Child Node has to be included
257  18 if(hasChild && !hideNodeChildInGoogleSitemap(child)) {
258    // Call recursively
259  4 feedSiteMapBeans(siteMapBeans, childNodes, isForEdit, null);
260    }
261   
262    }
263   
264    }
265   
266    }
267    }
268   
269    /**
270    * Populate the Bean.
271    */
 
272  33 toggle private SiteMapEntry populateBean( Node child, boolean isForUriMapping, boolean isForEdit, Site site, Boolean inheritedParentDisplayColor ) throws RepositoryException {
273  33 SiteMapEntry res = null;
274    // Populate only if true
275  33 if(hasToBePopulated(child, isForUriMapping, isForEdit, site)) {
276   
277  33 String lastmod = "";
278  33 int level = -1;
279  33 String loc = null;
280  33 double priority = DEFAULT_PRIORITY;
281  33 String changefreq = DEFAULT_CHANGE_FREQUENCY;
282    // Get Level
283  33 level = child.getDepth();
284   
285    // Get priority
286  33 if(child.hasProperty(GoogleSiteMapConfiguration.DEFAULT_PRIORITY_NODEDATA)) {
287  0 priority = child.getProperty(GoogleSiteMapConfiguration.DEFAULT_PRIORITY_NODEDATA).getDouble();
288    }
289   
290    // Get changefreq
291  33 if(child.hasProperty(GoogleSiteMapConfiguration.DEFAULT_CHANGEFREQ_NODEDATA)) {
292  0 changefreq = child.getProperty(GoogleSiteMapConfiguration.DEFAULT_CHANGEFREQ_NODEDATA).getString();
293    }
294   
295    // Get lastmod
296  33 Calendar date = null;
297  33 if (NodeTypes.LastModified.getLastModified(child) != null) {
298  33 date = NodeTypes.LastModified.getLastModified(child);
299    } else {
300  0 date = NodeTypes.Created.getCreated(child);
301    }
302  33 lastmod = configuration.getFastDateFormat().format(date.getTime());
303   
304    // Get loc
305  33 if(isForUriMapping) {
306  9 loc = child.hasProperty("fromURI")? MgnlContext.getContextPath()+child.getProperty("fromURI").getString() : "";
307    }else {
308  24 loc = LinkUtil.createExternalLink(child);
309    }
310   
311    //Populate the bean:
312  33 res = new SiteMapEntry(loc, lastmod, changefreq, Double.toString(priority), level);
313  33 log.debug("Populate Basic info for Node: "+child.getPath()+ " with values "+res.toStringDisplay());
314   
315  33 if(isForEdit) {
316  9 res = populateBeanForEdit(child, isForUriMapping, res, inheritedParentDisplayColor);
317    }
318    }
319   
320  33 return res;
321    }
322   
323   
324    /**
325    * Add additional info's for Edit.
326    */
 
327  9 toggle private SiteMapEntry populateBeanForEdit(Node child, boolean isForUriMapping, SiteMapEntry siteMapBean, Boolean inheritedParentDisplayColor) throws RepositoryException {
328    //Common fields
329  9 siteMapBean.setPath(child.getPath());
330   
331  9 if(isForUriMapping) {
332    //For VirtualUri
333  3 siteMapBean.setFrom(child.hasProperty("fromURI")?child.getProperty("fromURI").getString():"");
334  3 siteMapBean.setTo(child.hasProperty("toURI")?child.getProperty("toURI").getString():"");
335  3 siteMapBean.setStyleAlert(hideNodeInGoogleSitemap(child));
336  3 log.debug("Populate Edit VirtualUri info for Node: "+child.getPath()+ " with values "+siteMapBean.toStringVirtualUri());
337    } else {
338    //For Site
339  6 siteMapBean.setPageName(child.hasProperty("title")?child.getProperty("title").getString():"");
340  6 siteMapBean.setStyleAlert(inheritedParentDisplayColor!=null?inheritedParentDisplayColor.booleanValue():hideNodeInGoogleSitemap(child));
341  6 log.debug("Populate Edit Site info for Node: "+child.getPath()+ " with values "+siteMapBean.toStringSite());
342    }
343  9 return siteMapBean;
344    }
345   
346   
347    /**
348    * Check if the node has to be populated.
349    * True for UriMapping if:
350    * !hideInGoogleSitemap && node has a modification date
351    * True for Site if:
352    * !hideInGoogleSitemap && !hideInGoogleSitemapChildren && node has a creation date && site.isEnabled()
353    *
354    */
 
355  82 toggle private boolean hasToBePopulated( Node child, boolean isForUriMapping, boolean isForEdit, Site site ) throws RepositoryException {
356  82 boolean hasCreationDate = NodeTypes.Created.getCreated(child) != null;
357  82 boolean hasModificationDate = NodeTypes.LastModified.getLastModified(child) != null;
358  82 boolean hasTemplate = StringUtils.isNotBlank(NodeTypes.Renderable.getTemplate(child));
359   
360    // Exclude SiteMap pages
361  82 if (hasTemplate && NodeTypes.Renderable.getTemplate(child).endsWith(GoogleSiteMapConfiguration.SITE_MAP_TEMPLATE_NAME)) {
362  0 return false;
363    }
364   
365    // For edit and if has creation date --> true.
366  82 if(isForEdit) {
367  22 return hasCreationDate;
368    }
369   
370    // Node has a property set to hide node to be display --> false.
371  60 if(hideNodeInGoogleSitemap(child) && (isForUriMapping || hideNodeChildInGoogleSitemap(child))) {
372  1 return false;
373    }
374    // For uriMapping.
375  59 if(isForUriMapping && hasModificationDate) {
376  12 return true;
377    }
378    // For site.
379  47 if(!isForUriMapping && site.isEnabled() && hasCreationDate ) {
380  36 return true;
381    }
382   
383  11 return false;
384    }
385   
386    /**
387    * Check if we have to display in SiteMap.
388    */
 
389  86 toggle private boolean hideNodeInGoogleSitemap(Node child) throws RepositoryException {
390  86 boolean res = false;
391   
392  86 if(child.hasProperty(GoogleSiteMapConfiguration.DEFAULT_HIDEINGOOGLESITEMAP_NODEDATA)) {
393  6 res = child.getProperty(GoogleSiteMapConfiguration.DEFAULT_HIDEINGOOGLESITEMAP_NODEDATA).getBoolean();
394    }
395   
396  86 return res;
397    }
398   
399    /**
400    * Check if we have to display in SiteMap.
401    */
 
402  10 toggle private boolean hideNodeChildInGoogleSitemap(Node child) throws RepositoryException {
403  10 boolean res = false;
404   
405  10 if(child.hasProperty(GoogleSiteMapConfiguration.DEFAULT_HIDEINGOOGLESITEMAPCHILDREN_NODEDATA)) {
406  2 res = child.getProperty(GoogleSiteMapConfiguration.DEFAULT_HIDEINGOOGLESITEMAPCHILDREN_NODEDATA).getBoolean();
407    }
408   
409  10 return res;
410    }
411   
412   
413    /**
414    * Get Site and virtualUri informations if they are defined as components.
415    * Notice that by using a HashSet, a single object will be keeped in case of equality of the location.
416    */
 
417  7 toggle public Iterator<SiteMapEntry> getSiteMapBeans(Node content) throws RepositoryException {
418  7 Set<SiteMapEntry> res = new HashSet<SiteMapEntry>();
419    // Return if no content set.
420  7 if (!NodeUtil.isNodeType(content, GoogleSiteMapConfiguration.NODE_TYPE)) {
421  0 return res.iterator();
422    }
423   
424    // Display Site informations part... if defined
425  7 Node node = null;
426  7 Iterator<Node> iterator = NodeUtil.collectAllChildren(content, All_Site_Nodes).iterator();
427  12 while (iterator.hasNext()) {
428    // Get node
429  5 node = iterator.next();
430   
431  5 Node childNode = new JCRMgnlPropertiesFilteringNodeWrapper(node);
432  5 PropertyIterator properties = childNode.getProperties();
433   
434  16 while (properties.hasNext()) {
435  11 Property property = properties.nextProperty();
436  11 Node root = SessionUtil.getNode(RepositoryConstants.WEBSITE, property.getString());
437    // Call service and Remove Duplicate
438  11 res.addAll(getSiteMapBeanForSite(root, false));
439   
440    }
441    }
442   
443  7 boolean includeVirtualUris =
444    content.hasProperty(GoogleSiteMapConfiguration.INCLUDE_VIRTUAL_URI_MAPPINGS_PROPERTIES) &&
445    content.getProperty(GoogleSiteMapConfiguration.INCLUDE_VIRTUAL_URI_MAPPINGS_PROPERTIES).getBoolean();
446  7 if (includeVirtualUris) {
447  2 res.addAll(getSiteMapBeanForVirtualUri(false));
448    }
449  7 return res.iterator();
450    }
451   
452    }