View Javadoc

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