001/*
002 *  Copyright 2012 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016
017package org.ametys.cms.transformation.xslt;
018
019import java.util.ArrayList;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023
024import org.apache.avalon.framework.context.Context;
025import org.apache.avalon.framework.context.ContextException;
026import org.apache.avalon.framework.logger.LogEnabled;
027import org.apache.avalon.framework.logger.Logger;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.cocoon.components.ContextHelper;
031import org.apache.cocoon.environment.Request;
032import org.apache.commons.lang.StringUtils;
033import org.w3c.dom.Element;
034import org.w3c.dom.Node;
035import org.w3c.dom.NodeList;
036
037import org.ametys.cms.contenttype.ContentType;
038import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
039import org.ametys.cms.repository.Content;
040import org.ametys.cms.tag.Tag;
041import org.ametys.cms.tag.TagProviderExtensionPoint;
042import org.ametys.cms.transformation.dom.TagElement;
043import org.ametys.core.util.dom.AmetysNodeList;
044import org.ametys.core.util.dom.EmptyElement;
045import org.ametys.core.util.dom.StringElement;
046import org.ametys.plugins.explorer.resources.Resource;
047import org.ametys.plugins.explorer.resources.ResourceCollection;
048import org.ametys.plugins.explorer.resources.dom.ResourceCollectionElement;
049import org.ametys.plugins.repository.AmetysObjectResolver;
050import org.ametys.plugins.repository.AmetysRepositoryException;
051import org.ametys.plugins.repository.UnknownAmetysObjectException;
052import org.ametys.plugins.repository.metadata.CompositeMetadata;
053import org.ametys.plugins.repository.metadata.UnknownMetadataException;
054
055/**
056 * Helper component to be used from XSL stylesheets.
057 */
058public class AmetysXSLTHelper extends org.ametys.core.util.AmetysXSLTHelper implements LogEnabled
059{
060    /** The Ametys object resolver */
061    protected static AmetysObjectResolver _ametysObjectResolver;
062    /** The content types extension point */
063    protected static ContentTypeExtensionPoint _cTypeExtensionPoint;
064    /** The tags provider */
065    protected static TagProviderExtensionPoint _tagProviderExtPt;
066    /** The avalon context */
067    protected static Context _context;
068    /** The logger */
069    protected static Logger _logger;
070    
071    @Override
072    public void contextualize(Context context) throws ContextException
073    {
074        super.contextualize(context);
075        _context = context;
076    }
077    
078    @Override
079    public void enableLogging(Logger logger)
080    {
081        _logger = logger;
082    }
083    
084    @Override
085    public void service(ServiceManager manager) throws ServiceException
086    {
087        _ametysObjectResolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
088        _cTypeExtensionPoint = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE);
089        _tagProviderExtPt = (TagProviderExtensionPoint) manager.lookup(TagProviderExtensionPoint.ROLE);
090    }
091    
092    /* ------------------------ */
093    /*      Content methods     */
094    /* ------------------------ */
095    
096    /**
097     * Get the content types of a content
098     * @param contentId The content id
099     * @return The content type or empty if the content does not exist
100     */
101    public static NodeList contentTypes(String contentId)
102    {
103        ArrayList<StringElement> contentTypes = new ArrayList<>();
104        
105        try
106        {
107            Content content = _ametysObjectResolver.resolveById(contentId);
108            
109            try
110            {
111                for (String id : content.getTypes())
112                {
113                    contentTypes.add(new StringElement("content-type", "id", id));
114                }
115            }
116            catch (AmetysRepositoryException e)
117            {
118                _logger.error("Can not get type of content with id '" + contentId + "'", e);
119            }
120        }
121        catch (UnknownAmetysObjectException e)
122        {
123            _logger.error("Can not get type of content with id '" + contentId + "'", e);
124        }
125        
126        return new AmetysNodeList(contentTypes);
127    }
128    
129    /**
130     * Get the mixins of a content
131     * @param contentId The content id
132     * @return The content type or empty if the content does not exist
133     */
134    public static NodeList contentMixinTypes(String contentId)
135    {
136        ArrayList<StringElement> contentTypes = new ArrayList<>();
137        
138        try
139        {
140            Content content = _ametysObjectResolver.resolveById(contentId);
141            
142            try
143            {
144                for (String id : content.getMixinTypes())
145                {
146                    contentTypes.add(new StringElement("mixin", "id", id));
147                }
148            }
149            catch (AmetysRepositoryException e)
150            {
151                _logger.error("Can not get type of content with id '" + contentId + "'", e);
152            }
153        }
154        catch (UnknownAmetysObjectException e)
155        {
156            _logger.error("Can not get type of content with id '" + contentId + "'", e);
157        }
158        
159        return new AmetysNodeList(contentTypes);
160    }
161    
162    
163    /**
164     * Get the metadata of a content
165     * @param contentId The content id
166     * @param metadataName The metadata name (/ for composite)
167     * @return The name or empty if the metadata or the content does not exist
168     */
169    public static String contentMetadata(String contentId, String metadataName)
170    {
171        try
172        {
173            Content content = _ametysObjectResolver.resolveById(contentId);
174            try
175            {
176                return _getMetadata(content.getMetadataHolder(), metadataName);
177            }
178            catch (UnknownMetadataException e)
179            {
180                _logger.error("Can not get metadata '" + metadataName + "' on content with id '" + contentId + "'", e);
181                return "";
182            }
183        }
184        catch (UnknownAmetysObjectException e)
185        {
186            _logger.error("Can not get metadata '" + metadataName + "' on unknown content with id '" + contentId + "'", e);
187            return "";
188        }
189    }
190    
191    private static String _getMetadata(CompositeMetadata cm, String metadataName)
192    {
193        int i = metadataName.indexOf("/");
194        if (i == -1)
195        {
196            return cm.getString(metadataName);
197        }
198        else
199        {
200            return _getMetadata(cm.getCompositeMetadata(metadataName.substring(0, i)), metadataName.substring(i + 1));
201        }
202    }
203    
204    /**
205     * Returns a DOM {@link Element} containing {@link Resource}s representing the attachments of the current content.
206     * @return an Element containing the attachments of the current content as {@link Resource}s.
207     */
208    public static Node contentAttachments()
209    {
210        Request request = ContextHelper.getRequest(_context);
211        
212        Content content = (Content) request.getAttribute(Content.class.getName());
213        
214        return contentAttachments(content);
215    }
216    
217    /**
218     * Returns a DOM {@link Element} containing {@link Resource}s representing the attachments of a given content.
219     * @param contentId the content ID.
220     * @return an Element containing the attachments of the given content as {@link Resource}s.
221     */
222    public static Node contentAttachments(String contentId)
223    {
224        Content content = _ametysObjectResolver.resolveById(contentId);
225        
226        return contentAttachments(content);
227    }
228    
229    /**
230     * Returns a DOM {@link Element} containing {@link Resource}s representing the attachments of a given content.
231     * @param content the content.
232     * @return an Element containing the attachments of the given content as {@link Resource}s.
233     */
234    private static Node contentAttachments(Content content)
235    {
236        if (content == null)
237        {
238            return null;
239        }
240        
241        ResourceCollection collection = content.getRootAttachments();
242        
243        return collection != null ? new ResourceCollectionElement(collection) : new EmptyElement("collection");
244    }
245    
246  //*************************
247    // Tag methods
248    //*************************
249    
250    /**
251     * Returns all tags of the current content.
252     * @return a list of tags.
253     */
254    public static NodeList contentTags()
255    {
256        Request request = ContextHelper.getRequest(_context);
257        
258        Content content = (Content) request.getAttribute(Content.class.getName());
259        
260        if (content == null)
261        {
262            return null;
263        }
264        
265        List<TagElement> list = new ArrayList<>();
266        
267        for (String tag : content.getTags())
268        {
269            list.add(new TagElement(tag));
270        }
271        
272        return new AmetysNodeList(list);
273    }
274    
275    /**
276     * Get the name of the parent of a tag.
277     * @param siteName the site name
278     * @param tagName the tag's name
279     * @return The id of parent or empty if not found
280     */
281    public static String tagParent(String siteName, String tagName)
282    {
283        Map<String, Object> contextParameters = new HashMap<>();
284        contextParameters.put("siteName", siteName);
285        
286        Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters);
287        if (tag == null)
288        {
289            return StringUtils.EMPTY;
290        }
291        
292        String parentName = tag.getParentName();
293        return parentName != null ? parentName : StringUtils.EMPTY;
294    }
295    
296    /**
297     * Get the path of a tag. The path contains the tag's parents seprated by '/'.
298     * @param siteName The site name
299     * @param tagName The unique tag's name
300     * @return The tag's path or empty string if tag does not exist
301     */
302    public static String tagPath (String siteName, String tagName)
303    {
304        Map<String, Object> contextParameters = new HashMap<>();
305        contextParameters.put("siteName", siteName);
306        
307        Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters);
308        if (tag == null)
309        {
310            return StringUtils.EMPTY;
311        }
312        
313        String path = tagName;
314        
315        Tag parentTag = tag.getParent();
316        while (parentTag != null)
317        {
318            path = parentTag.getName() + "/" + path;
319            parentTag = parentTag.getParent();
320        }
321
322        return path;
323    }
324    
325    /**
326     * Get the label of a tag
327     * @param siteName the current site
328     * @param tagName the name of the tag
329     * @param lang the lang (if i18n tag)
330     * @return the label of the tag or empty if it cannot be found
331     */
332    public static String tagLabel(String siteName, String tagName, String lang)
333    {
334        Map<String, Object> contextParameters = new HashMap<>();
335        contextParameters.put("siteName", siteName);
336        
337        Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters);
338        return tag == null ? "" : _i18nUtils.translate(tag.getTitle(), lang);
339    }
340    
341    /**
342     * Get the description of a tag
343     * @param siteName the current site
344     * @param tagName the name of the tag
345     * @param lang the lang (if i18n tag)
346     * @return the label of the tag or empty if it cannot be found
347     */
348    public static String tagDescription(String siteName, String tagName, String lang)
349    {
350        Map<String, Object> contextParameters = new HashMap<>();
351        contextParameters.put("siteName", siteName);
352        
353        Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters);
354        return tag == null ? "" : _i18nUtils.translate(tag.getDescription(), lang);
355    }
356    
357    /**
358     * Get the visibility of a tag
359     * @param siteName the current site
360     * @param tagName the name of the tag
361     * @return the lower-cased visibility of the tag ("public" or "private")
362     */
363    public static String tagVisibility(String siteName, String tagName)
364    {
365        Map<String, Object> contextParameters = new HashMap<>();
366        contextParameters.put("siteName", siteName);
367        
368        Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters);
369        return tag == null ? "" : tag.getVisibility().toString().toLowerCase();
370    }
371    
372    /* ----------------------------- */
373    /*      Content type methods     */
374    /* ----------------------------- */
375    
376    /**
377     * Returns all tags of a content type
378     * @param contentTypeId The id of the content type
379     * @return a list of tags.
380     */
381    public static NodeList contentTypeTags(String contentTypeId)
382    {
383        ArrayList<TagElement> tags = new ArrayList<>();
384        
385        try
386        {
387            ContentType cType = _cTypeExtensionPoint.getExtension(contentTypeId);
388            if (cType != null)
389            {
390                for (String tag : cType.getTags())
391                {
392                    tags.add(new TagElement(tag));
393                }
394            }
395            else
396            {
397                _logger.error("Can not get tags of unknown content type of id '" + contentTypeId + "'");
398            }
399            
400        }
401        catch (AmetysRepositoryException e)
402        {
403            _logger.error("Can not get tags of content type of id '" + contentTypeId + "'", e);
404        }
405        
406        return new AmetysNodeList(tags);
407    }
408}