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