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         info.magnolia.module.site.Site site = siteManager.getCurrentSite();
221         if (site instanceof Site) {
222             return (Site) site;
223         }
224         return null;
225     }
226 
227     /**
228      * Returns the according {@link Site} of the passed content {@link Node}.
229      *
230      * @param content The {@link Node} to determine its {@link Site} from
231      * @return {@link Site} The according {@link Site} of the passed @param
232      * content
233      */
234     public Site site(Node content) {
235         try {
236             info.magnolia.module.site.Site site = siteManager.getAssignedSite(content);
237             if (site instanceof Site) {
238                 return (Site) site;
239             }
240             return null;
241         } catch (IllegalStateException e) {
242             log.error("Unable to access assigned site ({}) until following issue is resolved: {}", content.toString(), e.getMessage());
243             return null;
244         }
245     }
246 
247     // TODO dlipp/had: is that a good place to keep that code - you might have
248     // to access it from non-template related code. Maybe we shouldn't
249     // completely drop STKUtil but
250     // keep it for common stuff.
251     public Theme theme(Site site) {
252         final String theme = site.getTheme().getName();
253         return StringUtils.isBlank(theme) ? new ThemeImpl(new STKImagingSupport()) : (Theme) moduleProvider.get().getTheme(theme);
254     }
255 
256     public info.magnolia.module.site.theme.Theme theme(info.magnolia.module.site.Site site) {
257         final String theme = site.getTheme().getName();
258         return StringUtils.isBlank(theme) ? new ConfiguredTheme(null) : moduleProvider.get().getTheme(theme);
259     }
260 
261     /**
262      * Returns the according {@link Site} of the passed content {@link ContentMap}.
263      *
264      * @param content The {@link ContentMap} to determine its {@link Site} from
265      * @return {@link Site} The according {@link Site} of the passed @param
266      * content
267      */
268     public Site site(ContentMap content) {
269         return site(content.getJCRNode());
270     }
271 
272     /**
273      * Returns the site's root {@link Node} of the @param content {@link Node}.
274      * The root {@link Node} is defined as the page {@link Node} having template
275      * category TemplateCategory.HOME. If no ancestor page exists with category
276      * TemplateCategory.HOME, the JCR workspace root is returned.
277      *
278      * @param content The {@link Node} to determine its site root
279      * @return {@link Node} The site root {@link Node} of the passed content {@link Node}
280      */
281     public Node siteRoot(Node content) {
282         return this.siteRoot(content, SITEROOT_TEMPLATE_CATEGORY);
283     }
284 
285     /**
286      * 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
287      * ancestor page exists with category TemplateCategory.HOME, the JCR
288      * workspace root is returned.
289      *
290      * @param content The {@link ContentMap} to determine its site root
291      * @return {@link ContentMap} The site root {@link ContentMap} of the passed @param
292      * content
293      */
294     public ContentMap siteRoot(ContentMap content) {
295         return this.siteRoot(content, SITEROOT_TEMPLATE_CATEGORY);
296     }
297 
298     /**
299      * 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
300      * ancestor page exists with category TemplateCategory.HOME, the JCR
301      * workspace root is returned.
302      *
303      * @param content The {@link Node} to determine its site root
304      * @param siteRootTemplateCategory The {@link TemplateCategory} value of the site root to detect.
305      * If null or EMPTY is passed, the default value
306      * SITEROOT_TEMPLATE_CATEGORY is used instead.
307      * @return {@link Node} The site root {@link Node} of the passed content {@link Node}
308      */
309     public ContentMap siteRoot(ContentMap content, String siteRootTemplateCategory) {
310         return templatingFunctions.asContentMap(siteRoot(content.getJCRNode(), siteRootTemplateCategory));
311     }
312 
313     /**
314      * 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
315      * ancestor page exists with category TemplateCategory.HOME, the JCR
316      * workspace root is returned.
317      *
318      * @param content The {@link Node} to determine its site root
319      * @param siteRootTemplateCategory The {@link TemplateCategory} value of the site root to detect.
320      * If null or EMPTY is passed, the default value
321      * SITEROOT_TEMPLATE_CATEGORY is used instead.
322      * @return {@link Node} The site root {@link Node} of the passed content {@link Node}
323      */
324     public Node siteRoot(Node content, String siteRootTemplateCategory) {
325         if (StringUtils.isEmpty(siteRootTemplateCategory)) {
326             siteRootTemplateCategory = SITEROOT_TEMPLATE_CATEGORY;
327         }
328         try {
329             Node page = this.templatingFunctions.page(content);
330             Node root = TemplateCategoryUtil.findParentWithTemplateCategory(page, siteRootTemplateCategory);
331             return (root == null) ? (Node) page.getAncestor(0) : root;
332         } catch (RepositoryException e) {
333             throw new RuntimeException("Can't access site root.", e);
334         }
335     }
336 
337     public List<ContentMap> ancestorsInSite(ContentMap content) throws RepositoryException {
338         return ancestorsInSite(content, null);
339     }
340 
341     public List<Node> ancestorsInSite(Node content) throws RepositoryException {
342         return ancestorsInSite(content, null);
343     }
344 
345     public List<ContentMap> ancestorsInSite(ContentMap content, String nodeTypeName) throws RepositoryException {
346         return templatingFunctions.asContentMapList(ancestorsInSite(content.getJCRNode(), nodeTypeName));
347     }
348 
349     public List<Node> ancestorsInSite(Node content, String nodeTypeName) throws RepositoryException {
350         List<Node> allAncestors = templatingFunctions.ancestors(content, nodeTypeName);
351         Node siteRoot = siteRoot(content);
352 
353         List<Node> ancestoresInSite = new ArrayList<Node>();
354         for (Node current : allAncestors) {
355             if (current.getDepth() >= siteRoot.getDepth()) {
356                 ancestoresInSite.add(current);
357             }
358         }
359         return ancestoresInSite;
360     }
361 
362     /**
363      * @deprecated Since 2.5. Use {@link DamTemplatingFunctions#getAssetLinkForId(String)}
364      */
365     @Deprecated
366     public String getAssetLink(Node content, String assetProperty) {
367         return getAssetLink(content, assetProperty, ImagingSupport.VARIATION_ORIGINAL);
368     }
369 
370     /**
371      * @deprecated Since 2.5. Use {@link DamTemplatingFunctions#getAssetLinkForId(String)}
372      */
373     @Deprecated
374     public String getAssetLink(ContentMap content, String assetProperty) {
375         return getAssetLink(content.getJCRNode(), assetProperty);
376     }
377 
378     /**
379      * @deprecated Since 2.5. Use {@link DamTemplatingFunctions#getAssetLinkForId(String)
380      * (String)}
381      */
382     @Deprecated
383     public String getAssetLink(ContentMap content, String assetProperty, AbstractSTKTemplateModel<? extends TemplateDefinition> model) {
384         return getAssetLink(content.getJCRNode(), assetProperty, resolveImageVariationName(model));
385     }
386 
387     /**
388      * @deprecated Since 2.5. Use {@link DamTemplatingFunctions#getAssetLinkForId(String, String)}
389      */
390     @Deprecated
391     public String getAssetLink(ContentMap content, String assetProperty, String renditionName) {
392         return getAssetLink(content.getJCRNode(), assetProperty, renditionName);
393     }
394 
395     /**
396      * @deprecated Since 2.5. Use {@link DamTemplatingFunctions#getAssetLinkForId(String, String)}
397      */
398     @Deprecated
399     public String getAssetLink(Node content, String assetProperty, String renditionName) {
400 
401         String assetIdentifier = PropertyUtil.getString(content, assetProperty);
402         if (StringUtils.isNotBlank(assetIdentifier)) {
403             return damTemplatingFunctions.getAssetLinkForId(assetIdentifier, renditionName);
404         }
405         return null;
406     }
407 
408     /**
409      * Resolves an image variation name based on parameter values found in the model. If none is found, defaults to {@link ImagingSupport#VARIATION_ORIGINAL}.
410      *
411      * @see CssSelectorBuilder
412      * @see info.magnolia.module.templatingkit.templates.components.ImageModel
413      */
414     public String resolveImageVariationName(AbstractSTKTemplateModel<? extends TemplateDefinition> model) {
415         if (model == null) {
416             return ImagingSupport.VARIATION_ORIGINAL;
417         }
418         TemplateDefinition definition = model.getDefinition();
419         String variationName = (String) definition.getParameters().get("imageVariation");
420 
421         if (StringUtils.isNotEmpty(variationName)) {
422             return variationName;
423         }
424 
425         String cssSelector = cssSelectorBuilder.createCssSelector(model);
426         String imageClass = (String) definition.getParameters().get("imageClass");
427         if (StringUtils.isNotEmpty(imageClass)) {
428             cssSelector = cssSelector + " ." + imageClass.replace(" ", ".");
429         }
430 
431         try {
432             return getImagingSupport().resolveVariationName(cssSelector);
433         } catch (NoSuchRenditionException e) {
434             log.warn(e.getMessage());
435             return ImagingSupport.VARIATION_ORIGINAL;
436         }
437     }
438 
439     /**
440      * @see info.magnolia.module.templatingkit.dam.STKAssetRenderer . This implementation of AssetRenderer return a STKAssetRendition that extends {@link info.magnolia.dam.api.AssetDecorator}.
441      * @deprecated Since 2.5. Use {@link DamTemplatingFunctions#getAssetRendition(Asset, String)}
442      */
443     @Deprecated
444     public AssetRendition getAssetVariation(Asset asset, String renditionName) {
445         return damTemplatingFunctions.getRendition(asset.getItemKey().asString(), renditionName);
446     }
447 
448     /**
449      * FIXME review: should this function be provided by this class? FIXME
450      * I18nContentWrapper --> I18nNodeWrapper
451      */
452     public Node wrap(Node content) {
453         return (content == null) ? null : templatingFunctions.encode(new I18nNodeWrapper(content));
454     }
455 
456     /**
457      * @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.
458      */
459     @Deprecated
460     public List<ContentMap> getCategories(Node page) {
461         return categorizationSupport.getCategories(page);
462     }
463 
464     /**
465      * @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.
466      */
467     @Deprecated
468     public String getCategoryLink(Node page, String categoryName) {
469         return categorizationSupport.getCategoryLink(page, categoryName);
470     }
471 
472     /**
473      * FIXME review: should this function be provided by this class?
474      */
475     public List<Node> getContentListByTemplateCategorySubCategory(Node siteRoot, String category, String subCategory) throws RepositoryException {
476         return getContentListByTemplateCategorySubCategory(siteRoot, category, subCategory, Integer.MAX_VALUE, null, null);
477     }
478 
479     /**
480      * FIXME review: should this function be provided by this class?
481      */
482     public static List<Node> getContentListByTemplateCategorySubCategory(Node siteRoot, String category, String subCategory, int maxResultSize, String andClause, String orderBy)
483             throws RepositoryException {
484         return TemplateCategoryUtil.getContentListByTemplateCategorySubCategory(siteRoot, category, subCategory, maxResultSize, andClause, orderBy);
485     }
486 
487     /**
488      * FIXME review: should this function be provided by this class?
489      */
490     public Node getNearestContentByTemplateCategorySubCategory(Node siteRoot, String category, String subCategory, Node current) throws RepositoryException {
491         return TemplateCategoryUtil.getNearestContentByTemplateCategorySubCategory(siteRoot, category, subCategory, current);
492     }
493 
494     /**
495      * FIXME review: should this function be provided by this class?
496      */
497     public static List<Node> getContentListByTemplateName(Node searchRoot, String templateName) throws RepositoryException {
498         return TemplateCategoryUtil.getContentListByTemplateName(searchRoot, templateName);
499     }
500 
501     /**
502      * FIXME review: should this function be provided by this class?
503      */
504     public static List<Node> getContentListByTemplateName(Node searchRoot, String templateName, int maxResultSize, String andClause, String orderByClause) throws RepositoryException {
505         return TemplateCategoryUtil.getContentListByTemplateName(searchRoot, templateName, maxResultSize, andClause, orderByClause);
506     }
507 
508     /**
509      * FIXME review: should this function be provided by this class?
510      */
511     public static List<Node> getContentListByTemplateNames(Node searchRoot, Set<String> templateIds, int maxResultSize, String andClause, String orderByClause) throws RepositoryException {
512         return TemplateCategoryUtil.getContentListByTemplateNames(searchRoot, templateIds, maxResultSize, andClause, orderByClause);
513     }
514 
515     /**
516      * Returns a {@link Node} object which is referenced by its id, stored in
517      * the @param propertyName.
518      *
519      * @param content the node with a property containing the referenced id value.
520      * @param idPropertyName The name of the property which contains the id of the
521      * referenced {@link Node}.
522      * @param referencedWorkspace The workspace in which the referenced {@link Node} exists.
523      * @return the referenced {@link Node}
524      */
525     public Node getReferencedContent(Node content, String idPropertyName, String referencedWorkspace) throws RepositoryException {
526         if (content.hasProperty(idPropertyName)) {
527             String identifier = PropertyUtil.getString(content, idPropertyName);
528             return wrap(NodeUtil.getNodeByIdentifier(referencedWorkspace, identifier));
529         }
530         return null;
531     }
532 
533     /**
534      * @param node Content Node
535      * @param assetProperty Property name of the node containing the Asset identifier
536      * (composite key containing a reference of the Asset Provider)
537      * @param renditionName Name of the rendition. If not specified, the default rendition
538      * will be applied.
539      * @return The targeted Asset or null if not found or in case of Exception.
540      * @deprecated Since 2.5. Use {@link DamTemplatingFunctions#getAssetRenditionForAssetId(String, String)} Get the Asset specified by 'nodeDataPrefix' property name
541      * linked to 'node' for the specified 'rendition'.
542      */
543     public Asset getAsset(Node node, String assetProperty, String renditionName) {
544         try {
545             if (node.hasProperty(assetProperty)) {
546                 String assetNodeIdentifier = node.getProperty(assetProperty).getString();
547                 return damTemplatingFunctions.getAssetRenditionForAssetId(assetNodeIdentifier, renditionName);
548             }
549         } catch (RepositoryException e) {
550             log.warn("Could not find asset for '{}' at '{}'", new Object[]{assetProperty, NodeUtil.getNodePathIfPossible(node), e});
551             return null;
552         }
553 
554         return null;
555     }
556 
557     /**
558      * @deprecated Since 2.5. Use {@link DamTemplatingFunctions#getAsset(String)}
559      */
560     public Asset getAsset(Node content, String assetProperty) {
561         try {
562             if (content.hasProperty(assetProperty)) {
563                 String assetNodeIdentifier = content.getProperty(assetProperty).getString();
564                 return new LegacyAssetWrapper(damTemplatingFunctions.getAssetForId(assetNodeIdentifier));
565             }
566         } catch (RepositoryException e) {
567             log.warn("Could not find asset for '{}' at '{}'", new Object[]{assetProperty, NodeUtil.getNodePathIfPossible(content), e});
568         }
569         return null;
570     }
571 
572     /**
573      * @deprecated Since 2.5. Use {@link DamTemplatingFunctions#getAsset(String)}
574      */
575     public Asset getAsset(ContentMap content, String assetProperty) {
576         return getAsset(content.getJCRNode(), assetProperty);
577     }
578 
579     /**
580      * @deprecated Since 2.5. Use {@link DamTemplatingFunctions#getAssetRenditionForAssetId(String, String)}
581      */
582     public Asset getAsset(ContentMap content, String nodeDataPrefix, String variationName) {
583         return getAsset(content.getJCRNode(), nodeDataPrefix, variationName);
584     }
585 
586     /**
587      * @return A List of Assets found as element of the folder or an Empty list
588      * in case of exception or if no Asset are found under this folder.
589      * @deprecated Since 2.5. Use {@link DamTemplatingFunctions#getAssetsFromFolderId(String)}
590      */
591     public List<Asset> getAssetsFromFolder(Node node, String assetProperty) {
592 
593         try {
594             if (node.hasProperty(assetProperty)) {
595                 String folderIdentifier = node.getProperty(assetProperty).getString();
596                 final List<Asset> assets = damTemplatingFunctions.getAssetsFromFolderId(folderIdentifier);
597                 for (int i = 0; i < assets.size(); i++) {
598                     assets.set(i, new LegacyAssetWrapper(assets.get(i)));
599                 }
600                 return assets;
601             }
602         } catch (RepositoryException e) {
603             log.warn("Failed to get assets from folder.", e);
604         }
605 
606         return new ArrayList<Asset>();
607     }
608 
609     /**
610      * Only supports InternalAssets because only InternalAssets support JCR
611      * queries.
612      *
613      * @deprecated Since 2.5. Use {@link DamTemplatingFunctions#getItems(String, AssetQuery)}
614      */
615     @Deprecated
616     public List<Asset> getAssetsByQuery(String statement) {
617         AssetQuery assetQuery = new AssetQuery.Builder().withAdditionalQueryStatement(statement).build();
618         final List<Asset> assets = damTemplatingFunctions.getAssetsForFilter(assetQuery);
619         for (int i = 0; i < assets.size(); i++) {
620             assets.set(i, new LegacyAssetWrapper(assets.get(i)));
621         }
622         return assets;
623     }
624 
625     /**
626      * Return a Node List with a maximum number or result less or equal to
627      * maxResults.
628      */
629     public List<Node> cutList(List<Node> itemsList, final int maxResults) {
630         if (itemsList.size() > maxResults) {
631             return itemsList.subList(0, maxResults);
632         }
633         return itemsList;
634     }
635 
636     // TODO : use WordUtils.abbreviate and/or add tests
637     public String abbreviateString(String stringToCut, int size) {
638         return abbreviateString(stringToCut, size, " ...");
639     }
640 
641     public String abbreviateString(String stringToCut, int size, String closureString) {
642         if (stringToCut.length() > size) {
643             int sizeMinusClosure = size - closureString.length();
644             String cutString = StringUtils.left(stringToCut, sizeMinusClosure);
645 
646             // If the character after the cut was a space, no word was cut ->
647             // no cutting on last space needed.
648             String firstCharAfterCut = stringToCut.substring(sizeMinusClosure, sizeMinusClosure + 1);
649             if (!firstCharAfterCut.equals(" ")) {
650                 cutString = StringUtils.substringBeforeLast(cutString, " ");
651             }
652 
653             return cutString + closureString;
654 
655         }
656         return stringToCut;
657     }
658 
659     /**
660      * Increases the context bound counter and returns the value. This is for
661      * instance useful to create unique page wide ids.
662      */
663     public int count(String name) {
664         String attributeName = STKTemplatingFunctions.class.getName() + name;
665         if (!MgnlContext.hasAttribute(attributeName)) {
666             MgnlContext.setAttribute(attributeName, new MutableInt(0));
667         }
668         MutableInt counter = (MutableInt) MgnlContext.getAttribute(attributeName);
669         counter.increment();
670         return counter.intValue();
671     }
672 
673     public String getDivIdAbbreviation(String divID) {
674         return getDivIdAbbreviation(divID, "-");
675     }
676 
677     public String getDivIdAbbreviation(String divID, String delimiter) {
678         String result = StringUtils.EMPTY;
679         if (StringUtils.isNotEmpty(divID) && StringUtils.isNotEmpty(delimiter)) {
680             String[] values = divID.split("\\" + delimiter);
681             for (String value : values) {
682                 result += value.substring(0, 1);
683             }
684         }
685         return result;
686     }
687 
688     /**
689      * Return a property from the metaData of the node.
690      */
691     public String metaDataProperty(Node content, String property) {
692         return templatingFunctions.metaData(content, property);
693     }
694 
695     public String metaDataProperty(ContentMap content, String property) {
696         return templatingFunctions.metaData(content, property);
697     }
698 
699     /**
700      * Return the template id associated with this content.
701      */
702 
703     public String metaDataTemplate(Node content) {
704         try {
705             return NodeTypes.Renderable.getTemplate(content);
706         } catch (RepositoryException e) {
707             return null;
708         }
709     }
710 
711     public String metaDataTemplate(ContentMap content) {
712         return metaDataTemplate(content.getJCRNode());
713     }
714 
715     /**
716      * For test purpose only.
717      */
718     public void setCssSelectorBuilder(CssSelectorBuilder cssSelectorBuilder) {
719         this.cssSelectorBuilder = cssSelectorBuilder;
720     }
721 
722     /**
723      * For test purpose only.
724      */
725     public void setCategorizationSupport(CategorizationSupport categorizationSupport) {
726         this.categorizationSupport = categorizationSupport;
727     }
728 
729 
730     /**
731      * @see #getImageVariationLinkFromBinary(Node, String)
732      */
733     public String getImageVariationLinkFromBinary(ContentMap binaryContent, AbstractSTKTemplateModel<? extends TemplateDefinition> model) {
734         if (binaryContent == null) {
735             return null;
736         }
737         return getImageVariationLinkFromBinary(binaryContent.getJCRNode(), model);
738     }
739 
740     /**
741      * @see #getImageVariationLinkFromBinary(Node, String)
742      */
743     public String getImageVariationLinkFromBinary(Node binaryContent, AbstractSTKTemplateModel<? extends TemplateDefinition> model) {
744         return getImageVariationLinkFromBinary(binaryContent, resolveImageVariationName(model));
745     }
746 
747     /**
748      * @see #getImageVariationLinkFromBinary(Node, String)
749      */
750     public String getImageVariationLinkFromBinary(ContentMap binaryContent, String rendition) {
751         if (binaryContent == null) {
752             return null;
753         }
754         return getImageVariationLinkFromBinary(binaryContent.getJCRNode(), rendition);
755     }
756 
757     /**
758      * @return the link to an image variation for non asset nodes containing a binary
759      */
760     public String getImageVariationLinkFromBinary(Node binaryContent, String rendition) {
761         if (binaryContent == null || rendition == null) {
762             return null;
763         }
764 
765         try {
766             String link = getImagingSupport().createLink(binaryContent.getProperty(JcrConstants.JCR_DATA), rendition);
767             // URL encode the path because the user name node (=email address for ResearchAnt) in the path may contain illegal
768             // URL characters (like '+'). Fix for: https://jira.info.nl/browse/TOKUE-319
769             // for background info see: http://wiki.apache.org/jackrabbit/EncodingAndEscaping
770             return Text.escapePath(link);
771         } catch (Exception e) {
772             throw new DamException("AssetRendition exception", e) {
773             };
774         }
775     }
776 
777     protected ImagingSupport getImagingSupport() {
778         return (ImagingSupport) theme(site()).getImaging();
779     }
780 
781 }