View Javadoc

1   /**
2    * This file Copyright (c) 2008-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.templatingkit.functions;
35  
36  import info.magnolia.context.MgnlContext;
37  import info.magnolia.dam.DamManager;
38  import info.magnolia.dam.asset.Asset;
39  import info.magnolia.dam.asset.functions.DamTemplatingFunctions;
40  import info.magnolia.dam.asset.renderer.NoSuchRenditionException;
41  import info.magnolia.dam.provider.AssetProviderNotFoundException;
42  import info.magnolia.dam.providers.jcr.JcrAssetProvider;
43  import info.magnolia.jcr.util.ContentMap;
44  import info.magnolia.jcr.util.NodeTypes;
45  import info.magnolia.jcr.util.NodeUtil;
46  import info.magnolia.jcr.util.PropertyUtil;
47  import info.magnolia.jcr.wrapper.I18nNodeWrapper;
48  import info.magnolia.module.templatingkit.STKModule;
49  import info.magnolia.module.templatingkit.categorization.CategorizationSupport;
50  import info.magnolia.module.templatingkit.imaging.ImagingSupport;
51  import info.magnolia.module.templatingkit.sites.Site;
52  import info.magnolia.module.templatingkit.sites.SiteManager;
53  import info.magnolia.module.templatingkit.style.CssSelectorBuilder;
54  import info.magnolia.module.templatingkit.style.Theme;
55  import info.magnolia.module.templatingkit.style.ThemeImpl;
56  import info.magnolia.module.templatingkit.templates.AbstractSTKTemplateModel;
57  import info.magnolia.module.templatingkit.templates.category.TemplateCategory;
58  import info.magnolia.module.templatingkit.templates.category.TemplateCategoryUtil;
59  import info.magnolia.rendering.template.TemplateDefinition;
60  import info.magnolia.repository.RepositoryConstants;
61  import info.magnolia.templating.functions.TemplatingFunctions;
62  
63  import java.util.ArrayList;
64  import java.util.List;
65  import java.util.Set;
66  
67  import javax.inject.Inject;
68  import javax.inject.Provider;
69  import javax.inject.Singleton;
70  import javax.jcr.Node;
71  import javax.jcr.RepositoryException;
72  
73  import org.apache.commons.lang.StringUtils;
74  import org.apache.commons.lang.mutable.MutableInt;
75  import org.slf4j.Logger;
76  import org.slf4j.LoggerFactory;
77  
78  /**
79   * This is an component exposing a couple of methods useful for STK templates.
80   * It's exposed in templates as "stkfn".
81   */
82  @Singleton
83  public class STKTemplatingFunctions {
84  
85      private static Logger log = LoggerFactory.getLogger(STKTemplatingFunctions.class);
86  
87      public static final String SITEROOT_TEMPLATE_CATEGORY = TemplateCategory.HOME;
88  
89      protected static final String TITLE_PROP_NAME = "title";
90      protected static final String SITE_TITLE_PROP_NAME = "siteTitle";
91  
92      // Injected variables
93      private CssSelectorBuilder cssSelectorBuilder;
94      private CategorizationSupport categorizationSupport;
95      private final SiteManager siteManager;
96      private final TemplatingFunctions templatingFunctions;
97      private final Provider<STKModule> moduleProvider;
98      private final DamTemplatingFunctions damFunctions;
99      private final DamManager damManager;
100 
101     @Inject
102     public STKTemplatingFunctions(TemplatingFunctions templatingFunctions, CssSelectorBuilder cssSelectorBuilder, CategorizationSupport categorizationSupport, SiteManager siteManager,
103             Provider<STKModule> moduleProvider, DamManager damManager, DamTemplatingFunctions damFunctions) {
104         this.templatingFunctions = templatingFunctions;
105         this.cssSelectorBuilder = cssSelectorBuilder;
106         this.categorizationSupport = categorizationSupport;
107         this.siteManager = siteManager;
108         this.moduleProvider = moduleProvider;
109         this.damFunctions = damFunctions;
110         this.damManager = damManager;
111     }
112 
113     /**
114      * Method used to get the SearchPageLink. This Search page Link is defined
115      * on the Main PageInfo/Site configuration (Search Page Path to search
116      * result page). Method is used from a STKComponent and STKArea Model.
117      *
118      * @param content
119      *            : Current Content node.
120      * @return relative path to the defined result page i.e.
121      *         '/magnolia-empty-webapp/demo-project/service/search-result.html'
122      */
123     public String searchPageLink(Node content) {
124         String searchResultPageUUID = PropertyUtil.getString(siteRoot(content), "searchUUID");
125         return (StringUtils.isEmpty(searchResultPageUUID)) ? null : templatingFunctions.link(RepositoryConstants.WEBSITE, searchResultPageUUID);
126     }
127 
128     public String searchPageLink(ContentMap content) {
129         return searchPageLink(content.getJCRNode());
130     }
131 
132     /**
133      * Return the Home Site Name = the physical node's object name.
134      */
135     public String homeName(Node content) {
136         return NodeUtil.getName(siteRoot(content));
137     }
138 
139     public String homeName(ContentMap content) {
140         return homeName(content.getJCRNode());
141     }
142 
143     /**
144      * Return the HomeTitle value. Referred by the property 'name' of the root
145      * Node
146      */
147     public String homeTitle(Node content) {
148         return PropertyUtil.getString(siteRoot(content), TITLE_PROP_NAME);
149     }
150 
151     public String homeTitle(ContentMap content) {
152         return homeTitle(content.getJCRNode());
153     }
154 
155     /**
156      * Return the SiteTitle value. Referred by the property 'siteTitle' of the
157      * root Node
158      */
159     public String siteTitle(Node content) {
160         return PropertyUtil.getString(siteRoot(content), SITE_TITLE_PROP_NAME);
161     }
162 
163     public String siteTitle(ContentMap content) {
164         return siteTitle(content.getJCRNode());
165     }
166 
167     /**
168      * Return the home link of this site.
169      */
170     public String homeLink(Node content) {
171         return templatingFunctions.link(siteRoot(content));
172     }
173 
174     public String homeLink(ContentMap content) {
175         return homeLink(content.getJCRNode());
176     }
177 
178     public Site site() {
179         return siteManager.getCurrentSite();
180     }
181 
182     /**
183      * Returns the according {@link Site} of the passed content {@link Node}.
184      *
185      * @param content
186      *            The {@link Node} to determine its {@link Site} from
187      * @return {@link Site} The according {@link Site} of the passed @param
188      *         content
189      */
190     public Site site(Node content) {
191         try {
192             return (content == null) ? null : siteManager.getAssignedSite(content);
193         } catch (IllegalStateException e) {
194             log.error("Unable to access assigned site ({}) until following issue is resolved: {}", content.toString(), e.getMessage());
195             return null;
196         }
197     }
198 
199     // TODO dlipp/had: is that a good place to keep that code - you might have
200     // to access it from non-template related code. Maybe we shouldn't
201     // completely drop STKUtil but
202     // keep it for common stuff.
203     public Theme theme(Site site) {
204         final String theme = site.getTheme().getName();
205         return StringUtils.isBlank(theme) ? new ThemeImpl() : moduleProvider.get().getTheme(theme);
206 
207     }
208 
209     /**
210      * Returns the according {@link Site} of the passed content
211      * {@link ContentMap}.
212      *
213      * @param content
214      *            The {@link ContentMap} to determine its {@link Site} from
215      * @return {@link Site} The according {@link Site} of the passed @param
216      *         content
217      */
218     public Site site(ContentMap content) {
219         return site(content.getJCRNode());
220     }
221 
222     /**
223      * Returns the site's root {@link Node} of the @param content {@link Node}.
224      * The root {@link Node} is defined as the page {@link Node} having template
225      * category TemplateCategory.HOME. If no ancestor page exists with category
226      * TemplateCategory.HOME, the JCR workspace root is returned.
227      *
228      * @param content
229      *            The {@link Node} to determine its site root
230      * @return {@link Node} The site root {@link Node} of the passed content
231      *         {@link Node}
232      */
233     public Node siteRoot(Node content) {
234         return this.siteRoot(content, SITEROOT_TEMPLATE_CATEGORY);
235     }
236 
237     /**
238      * Returns the site's root {@link ContentMap} of the @param content
239      * {@link ContentMap}. The root {@link ContentMap} is defined as the page
240      * {@link Node} having template category TemplateCategory.HOME. If no
241      * ancestor page exists with category TemplateCategory.HOME, the JCR
242      * workspace root is returned.
243      *
244      * @param content
245      *            The {@link ContentMap} to determine its site root
246      * @return {@link ContentMap} The site root {@link ContentMap} of the passed @param
247      *         content
248      */
249     public ContentMap siteRoot(ContentMap content) {
250         return this.siteRoot(content, SITEROOT_TEMPLATE_CATEGORY);
251     }
252 
253     /**
254      * Returns the site's root {@link Node} of the passed @param content
255      * {@link Node}. The root {@link Node} is defined as the page {@link Node}
256      * with template its category as @param siteRootTemplateCategory. If no
257      * ancestor page exists with category TemplateCategory.HOME, the JCR
258      * workspace root is returned.
259      *
260      * @param content
261      *            The {@link Node} to determine its site root
262      * @param siteRootTemplateCategory
263      *            The {@link TemplateCategory} value of the site root to detect.
264      *            If null or EMPTY is passed, the default value
265      *            SITEROOT_TEMPLATE_CATEGORY is used instead.
266      * @return {@link Node} The site root {@link Node} of the passed content
267      *         {@link Node}
268      */
269     public ContentMap siteRoot(ContentMap content, String siteRootTemplateCategory) {
270         return templatingFunctions.asContentMap(siteRoot(content.getJCRNode(), siteRootTemplateCategory));
271     }
272 
273     /**
274      *
275      * Returns the site's root {@link Node} of the passed @param content
276      * {@link Node}. The root {@link Node} is defined as the page {@link Node}
277      * with template its category as @param siteRootTemplateCategory. If no
278      * ancestor page exists with category TemplateCategory.HOME, the JCR
279      * workspace root is returned.
280      *
281      * @param content
282      *            The {@link Node} to determine its site root
283      * @param siteRootTemplateCategory
284      *            The {@link TemplateCategory} value of the site root to detect.
285      *            If null or EMPTY is passed, the default value
286      *            SITEROOT_TEMPLATE_CATEGORY is used instead.
287      * @return {@link Node} The site root {@link Node} of the passed content
288      *         {@link Node}
289      */
290     public Node siteRoot(Node content, String siteRootTemplateCategory) {
291         if (StringUtils.isEmpty(siteRootTemplateCategory)) {
292             siteRootTemplateCategory = SITEROOT_TEMPLATE_CATEGORY;
293         }
294         try {
295             Node page = this.templatingFunctions.page(content);
296             Node root = TemplateCategoryUtil.findParentWithTemplateCategory(page, siteRootTemplateCategory);
297             return (root == null) ? (Node) page.getAncestor(0) : root;
298         } catch (RepositoryException e) {
299             throw new RuntimeException("Can't access site root.", e);
300         }
301     }
302 
303     public List<ContentMap> ancestorsInSite(ContentMap content) throws RepositoryException {
304         return ancestorsInSite(content, null);
305     }
306 
307     public List<Node> ancestorsInSite(Node content) throws RepositoryException {
308         return ancestorsInSite(content, null);
309     }
310 
311     public List<ContentMap> ancestorsInSite(ContentMap content, String nodeTypeName) throws RepositoryException {
312         return templatingFunctions.asContentMapList(ancestorsInSite(content.getJCRNode(), nodeTypeName));
313     }
314 
315     public List<Node> ancestorsInSite(Node content, String nodeTypeName) throws RepositoryException {
316         List<Node> allAncestors = templatingFunctions.ancestors(content, nodeTypeName);
317         Node siteRoot = siteRoot(content);
318 
319         List<Node> ancestoresInSite = new ArrayList<Node>();
320         for (Node current : allAncestors) {
321             if (current.getDepth() >= siteRoot.getDepth()) {
322                 ancestoresInSite.add(current);
323             }
324         }
325         return ancestoresInSite;
326     }
327 
328     /**
329      * @deprecated Since 2.5. Use
330      *             {@link DamTemplatingFunctions#getAssetLinkForId(String)}
331      */
332     public String getAssetLink(Node content, String assetProperty) {
333         return getAssetLink(content, assetProperty, ImagingSupport.VARIATION_ORIGINAL);
334     }
335 
336     /**
337      * @deprecated Since 2.5. Use
338      *             {@link DamTemplatingFunctions#getAssetLinkForId(String)}
339      */
340     public String getAssetLink(ContentMap content, String assetProperty) {
341         return getAssetLink(content.getJCRNode(), assetProperty);
342     }
343 
344     /**
345      * @deprecated Since 2.5. Use
346      *             {@link DamTemplatingFunctions#getAssetLinkForId(String)
347      *             (String)}
348      */
349     public String getAssetLink(ContentMap content, String assetProperty, AbstractSTKTemplateModel<? extends TemplateDefinition> model) {
350         return getAssetLink(content.getJCRNode(), assetProperty, resolveImageVariationName(model));
351     }
352 
353     /**
354      * @deprecated Since 2.5. Use
355      *             {@link DamTemplatingFunctions#getAssetLinkForId(String, String)}
356      */
357     public String getAssetLink(ContentMap content, String assetProperty, String renditionName) {
358         return getAssetLink(content.getJCRNode(), assetProperty, renditionName);
359     }
360 
361     /**
362      * @deprecated Since 2.5. Use
363      *             {@link DamTemplatingFunctions#getAssetLinkForId(String, String)}
364      */
365     public String getAssetLink(Node content, String assetProperty, String renditionName) {
366 
367         String assetIdentifier = PropertyUtil.getString(content, assetProperty);
368         if (StringUtils.isNotBlank(assetIdentifier)) {
369             return damFunctions.getAssetLinkForId(assetIdentifier, renditionName);
370         }
371         return null;
372     }
373 
374     public String resolveImageVariationName(AbstractSTKTemplateModel<? extends TemplateDefinition> model) {
375         final String cssSelector = cssSelectorBuilder.createCssSelector(model);
376         try {
377             return theme(site(model.getNode())).getImaging().resolveVariationName(cssSelector);
378         } catch (NoSuchRenditionException e) {
379             log.warn(e.getMessage());
380             return ImagingSupport.VARIATION_ORIGINAL;
381         }
382     }
383 
384     /**
385      * @deprecated Since 2.5. Use
386      *             {@link DamTemplatingFunctions#getAssetRendition(Asset, String)}
387      */
388     public Asset getAssetVariation(Asset asset, String renditionName) {
389         return damFunctions.getAssetRendition(asset, renditionName);
390     }
391 
392     /**
393      * FIXME review: should this function be provided by this class? FIXME
394      * I18nContentWrapper --> I18nNodeWrapper
395      */
396     public Node wrap(Node content) {
397         return (content == null) ? null : templatingFunctions.encode(new I18nNodeWrapper(content));
398     }
399 
400     /**
401      * FIXME review: should this function be provided by this class?
402      */
403     public List<ContentMap> getCategories(Node page) {
404         return categorizationSupport.getCategories(page);
405     }
406 
407     /**
408      * FIXME review: should this function be provided by this class?
409      */
410     public String getCategoryLink(Node page, String categoryName) {
411         return categorizationSupport.getCategoryLink(page, categoryName);
412     }
413 
414     /**
415      * FIXME review: should this function be provided by this class?
416      */
417     public List<Node> getContentListByTemplateCategorySubCategory(Node siteRoot, String category, String subCategory) throws RepositoryException {
418         return getContentListByTemplateCategorySubCategory(siteRoot, category, subCategory, Integer.MAX_VALUE, null, null);
419     }
420 
421     /**
422      * FIXME review: should this function be provided by this class?
423      */
424     public static List<Node> getContentListByTemplateCategorySubCategory(Node siteRoot, String category, String subCategory, int maxResultSize, String andClause, String orderBy)
425             throws RepositoryException {
426         return TemplateCategoryUtil.getContentListByTemplateCategorySubCategory(siteRoot, category, subCategory, maxResultSize, andClause, orderBy);
427     }
428 
429     /**
430      * FIXME review: should this function be provided by this class?
431      */
432     public Node getNearestContentByTemplateCategorySubCategory(Node siteRoot, String category, String subCategory, Node current) throws RepositoryException {
433         return TemplateCategoryUtil.getNearestContentByTemplateCategorySubCategory(siteRoot, category, subCategory, current);
434     }
435 
436     /**
437      * FIXME review: should this function be provided by this class?
438      */
439     public static List<Node> getContentListByTemplateName(Node searchRoot, String templateName) throws RepositoryException {
440         return TemplateCategoryUtil.getContentListByTemplateName(searchRoot, templateName);
441     }
442 
443     /**
444      * FIXME review: should this function be provided by this class?
445      */
446     public static List<Node> getContentListByTemplateName(Node searchRoot, String templateName, int maxResultSize, String andClause, String orderByClause) throws RepositoryException {
447         return TemplateCategoryUtil.getContentListByTemplateName(searchRoot, templateName, maxResultSize, andClause, orderByClause);
448     }
449 
450     /**
451      * FIXME review: should this function be provided by this class?
452      */
453     public static List<Node> getContentListByTemplateNames(Node searchRoot, Set<String> templateIds, int maxResultSize, String andClause, String orderByClause) throws RepositoryException {
454         return TemplateCategoryUtil.getContentListByTemplateNames(searchRoot, templateIds, maxResultSize, andClause, orderByClause);
455     }
456 
457     /**
458      * Returns a {@link Node} object which is referenced by its id, stored in
459      * the @param propertyName.
460      *
461      * @param content
462      *            the node with a property containing the referenced id value.
463      * @param idPropertyName
464      *            The name of the property which contains the id of the
465      *            referenced {@link Node}.
466      * @param referencedWorkspace
467      *            The workspace in which the referenced {@link Node} exists.
468      * @return the referenced {@link Node}
469      */
470     public Node getReferencedContent(Node content, String idPropertyName, String referencedWorkspace) throws RepositoryException {
471         if (content.hasProperty(idPropertyName)) {
472             String identifier = PropertyUtil.getString(content, idPropertyName);
473             return wrap(NodeUtil.getNodeByIdentifier(referencedWorkspace, identifier));
474         }
475         return null;
476     }
477 
478     /**
479      * @deprecated Since 2.5. Use
480      *             {@link DamTemplatingFunctions#getAssetRenditionForAssetId(String, String)}
481      *
482      *             Get the Asset specified by 'nodeDataPrefix' property name
483      *             linked to 'node' for the specified 'rendition'.
484      *
485      * @param node
486      *            Content Node
487      * @param nodeDataPrefix
488      *            Property name of the node containing the Asset identifier
489      *            (composite key containing a reference of the Asset Provider)
490      * @param renditionName
491      *            Name of the rendition. If not specified, the default rendition
492      *            will be applied.
493      * @return The targeted Asset or null if not found or in case of Exception.
494      */
495     public Asset getAsset(Node node, String assetProperty, String renditionName) {
496         Asset damAsset = null;
497         String providerId = null;
498         try {
499             if (node.hasProperty(assetProperty)) {
500                 String assetNodeIdentifier = node.getProperty(assetProperty).getString();
501                 return damFunctions.getAssetRenditionForAssetId(assetNodeIdentifier, renditionName);
502             }
503         } catch (RepositoryException e) {
504             log.warn("Could not find asset for '{}' at '{}'", new Object[] { assetProperty, NodeUtil.getNodePathIfPossible(node), e });
505             return null;
506         }
507         return null;
508     }
509 
510     /**
511      * @deprecated Since 2.5. Use
512      *             {@link DamTemplatingFunctions#getAssetForId(String)}
513      */
514     public Asset getAsset(Node content, String assetProperty) {
515         return getAsset(content, assetProperty, ImagingSupport.VARIATION_ORIGINAL);
516     }
517 
518     /**
519      * @deprecated Since 2.5. Use
520      *             {@link DamTemplatingFunctions#getAssetForId(String)}
521      */
522     public Asset getAsset(ContentMap content, String assetProperty) {
523         return getAsset(content.getJCRNode(), assetProperty);
524     }
525 
526     /**
527      * @deprecated Since 2.5. Use
528      *             {@link DamTemplatingFunctions#getAssetForId(String)}
529      */
530     public Asset getAsset(ContentMap content, String nodeDataPrefix, String variationName){
531         return getAsset(content.getJCRNode(), nodeDataPrefix, variationName);
532     }
533 
534     /**
535      * @deprecated Since 2.5. Use
536      *             {@link DamTemplatingFunctions#getAssetsFromFolderId(String)}
537      *
538      *
539      * @return A List of Assets found as element of the folder or an Empty list
540      *         in case of exception or if no Asset are found under this folder.
541      */
542     public List<Asset> getAssetsFromFolder(Node node, String assetProperty) {
543 
544         try {
545             if (node.hasProperty(assetProperty)) {
546                 String folderIdentifier = node.getProperty(assetProperty).getString();
547                 return damFunctions.getAssetsFromFolderId(folderIdentifier);
548             }
549         } catch (RepositoryException e) {
550             log.warn("Failed to get assets from folder.", e);
551         }
552 
553         return new ArrayList<Asset>();
554     }
555 
556     /**
557      * Only supports InternalAssets because only InternalAssets support JCR
558      * queries.
559      *
560      * @deprecated Since 2.5. Use
561      *             {@link DamManager#getAssetsForFilter(info.magnolia.dam.asset.AssetFilter)}
562      */
563     public List<Asset> getAssetsByQuery(String statement) {
564         try {
565             JcrAssetProvider assetProvider = (JcrAssetProvider) damManager.getAssetProvider(JcrAssetProvider.PROVIDER_ID);
566             return assetProvider.getAssetsByQuery(statement, false);
567         } catch (AssetProviderNotFoundException e) {
568             log.warn("Failed to query for assets.", e);
569             return new ArrayList<Asset>();
570         }
571     }
572 
573     /**
574      * Return a Node List with a maximum number or result less or equal to
575      * maxResults.
576      */
577     public List<Node> cutList(List<Node> itemsList, final int maxResults) {
578         if (itemsList.size() > maxResults) {
579             return itemsList.subList(0, maxResults);
580         }
581         return itemsList;
582     }
583 
584     // TODO : use WordUtils.abbreviate and/or add tests
585     public String abbreviateString(String stringToCut, int size) {
586         return abbreviateString(stringToCut, size, " ...");
587     }
588 
589     public String abbreviateString(String stringToCut, int size, String closureString) {
590         if (stringToCut.length() > size) {
591             int sizeMinusClosue = size - closureString.length();
592             String cuttedString = StringUtils.left(stringToCut, sizeMinusClosue);
593 
594             // If the character after the cut was a space, no word was cutted ->
595             // no cutting on last space needed.
596             String firstCharAfterCut = stringToCut.substring(sizeMinusClosue, sizeMinusClosue + 1);
597             if (!firstCharAfterCut.equals(" ")) {
598                 cuttedString = StringUtils.substringBeforeLast(cuttedString, " ");
599             }
600 
601             return cuttedString + closureString;
602 
603         }
604         return stringToCut;
605     }
606 
607     /**
608      * Increases the context bound counter and returns the value. This is for
609      * instance useful to create unique page wide ids.
610      */
611     public int count(String name) {
612         String attributeName = STKTemplatingFunctions.class.getName() + name;
613         if (!MgnlContext.hasAttribute(attributeName)) {
614             MgnlContext.setAttribute(attributeName, new MutableInt(0));
615         }
616         MutableInt counter = (MutableInt) MgnlContext.getAttribute(attributeName);
617         counter.increment();
618         return counter.intValue();
619     }
620 
621     public String getDivIdAbbreviation(String divID) {
622         return getDivIdAbbreviation(divID, "-");
623     }
624 
625     public String getDivIdAbbreviation(String divID, String delimiter) {
626         String result = StringUtils.EMPTY;
627         if (StringUtils.isNotEmpty(divID) && StringUtils.isNotEmpty(delimiter)) {
628             String[] values = divID.split("\\" + delimiter);
629             for (String value : values) {
630                 result += value.substring(0, 1);
631             }
632         }
633         return result;
634     }
635 
636     /**
637      * Return a property from the metaData of the node.
638      */
639     public String metaDataProperty(Node content, String property) {
640         return templatingFunctions.metaData(content, property);
641     }
642 
643     public String metaDataProperty(ContentMap content, String property) {
644         return templatingFunctions.metaData(content, property);
645     }
646 
647     /**
648      * Return the template id associated with this content.
649      */
650 
651     public String metaDataTemplate(Node content) {
652         try {
653             return NodeTypes.Renderable.getTemplate(content);
654         } catch (RepositoryException e) {
655             return null;
656         }
657     }
658 
659     public String metaDataTemplate(ContentMap content) {
660         return metaDataTemplate(content.getJCRNode());
661     }
662 
663     /**
664      * For test purpose only.
665      */
666     public void setCssSelectorBuilder(CssSelectorBuilder cssSelectorBuilder) {
667         this.cssSelectorBuilder = cssSelectorBuilder;
668     }
669 
670     /**
671      * For test purpose only.
672      */
673     public void setCategorizationSupport(CategorizationSupport categorizationSupport) {
674         this.categorizationSupport = categorizationSupport;
675     }
676 
677 }