001/*
002 *  Copyright 2013 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 */
016package org.ametys.cms.content;
017
018import java.io.IOException;
019import java.util.Comparator;
020import java.util.HashMap;
021import java.util.Locale;
022import java.util.Map;
023import java.util.Set;
024import java.util.TreeSet;
025
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.cocoon.ProcessingException;
029import org.apache.cocoon.environment.ObjectModelHelper;
030import org.apache.cocoon.environment.Request;
031import org.apache.cocoon.generation.ServiceableGenerator;
032import org.apache.cocoon.xml.AttributesImpl;
033import org.apache.cocoon.xml.XMLUtils;
034import org.apache.commons.lang3.StringUtils;
035import org.xml.sax.SAXException;
036
037import org.ametys.cms.contenttype.ContentType;
038import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
039import org.ametys.cms.repository.Content;
040import org.ametys.cms.repository.DefaultContent;
041import org.ametys.cms.tag.Tag;
042import org.ametys.cms.tag.TagProviderExtensionPoint;
043import org.ametys.core.right.RightManager;
044import org.ametys.core.right.RightManager.RightResult;
045import org.ametys.plugins.repository.AmetysObjectResolver;
046import org.ametys.plugins.repository.version.VersionableAmetysObject;
047import org.ametys.runtime.i18n.I18nizableText;
048
049/**
050 * Generates addition information on content
051 */
052public class AdditionalContentPropertiesGenerator extends ServiceableGenerator
053{
054    /** The AmetysObject resolver. */
055    protected AmetysObjectResolver _resolver;
056    /** the Ametys rights manager */
057    protected RightManager _rightManager;
058    private ContentTypeExtensionPoint _contentTypeEP;
059    private TagProviderExtensionPoint _tagProviderEP;
060    
061    @Override
062    public void service(ServiceManager serviceManager) throws ServiceException
063    {
064        super.service(serviceManager);
065        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
066        _contentTypeEP = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE);
067        _tagProviderEP = (TagProviderExtensionPoint) serviceManager.lookup(TagProviderExtensionPoint.ROLE);
068        _rightManager = (RightManager) serviceManager.lookup(RightManager.ROLE);
069    }
070    
071    @Override
072    public void generate() throws IOException, SAXException, ProcessingException
073    {
074        Request request = ObjectModelHelper.getRequest(objectModel);
075        
076        String contentId = parameters.getParameter("contentId", request.getParameter("contentId"));
077        
078        Content content = null;
079        if (StringUtils.isNotEmpty(contentId))
080        {
081            content = _resolver.resolveById(contentId);
082        }
083        else
084        {
085            content = (Content) request.getAttribute(Content.class.getName());
086        }
087        
088        contentHandler.startDocument();
089        
090        AttributesImpl attrs = new AttributesImpl();
091        attrs.addCDATAAttribute("id", content.getId());
092        if (content instanceof DefaultContent)
093        {
094            try
095            {
096                DefaultContent dc = (DefaultContent) content;
097                attrs.addCDATAAttribute("path", dc.getNode().getPath());
098            }
099            catch (Exception e)
100            {
101                getLogger().error("Cannot determine JCR path for content " + content.getId(), e);
102            }
103        }
104        XMLUtils.startElement(contentHandler, "content", attrs);
105        
106        // Take the HEAD revision
107        String revision = null;
108        if (content instanceof VersionableAmetysObject)
109        {
110            revision = ((VersionableAmetysObject) content).getRevision();
111            ((VersionableAmetysObject) content).switchToRevision(null);
112        }
113        
114        _saxContentTypesHierarchy (content);
115        _saxMixinsHierarchy(content);
116        _saxReferencingContents(content);
117        _saxTags(content);
118        _saxOtherProperties(content);
119        _saxRepositoryRights();
120        
121        if (content instanceof VersionableAmetysObject && revision != null)
122        {
123            ((VersionableAmetysObject) content).switchToRevision(revision);
124        }
125        
126        XMLUtils.endElement(contentHandler, "content");
127        contentHandler.endDocument();
128    }
129    
130    private void _saxRepositoryRights() throws SAXException
131    {
132        XMLUtils.startElement(contentHandler, "repository");
133        XMLUtils.startElement(contentHandler, "rights");
134        XMLUtils.createElement(contentHandler, "access", Boolean.toString(_rightManager.currentUserHasRight("REPOSITORY_Rights_Access", "/repository") == RightResult.RIGHT_ALLOW));
135        XMLUtils.endElement(contentHandler, "rights");
136        XMLUtils.endElement(contentHandler, "repository");
137        
138    }
139
140    /**
141     * SAX other additional properties
142     * @param content the content 
143     * @throws SAXException if an error occurred while saxing
144     */
145    protected void _saxOtherProperties (Content content) throws SAXException
146    {
147        // Nothing to do
148    }
149    
150    /**
151     * SAX content's tags
152     * @param content The content
153     * @throws SAXException if an error occurred
154     */
155    protected void _saxTags (Content content) throws SAXException
156    {
157        Set<String> tags = content.getTags();
158        
159        XMLUtils.startElement(contentHandler, "tags");
160        
161        for (String tagName : tags)
162        {
163            Tag tag = _tagProviderEP.getTag(tagName, getContextualParameters(content));
164            if (tag != null)
165            {
166                AttributesImpl attrs = new AttributesImpl();
167                attrs.addCDATAAttribute("name", tag.getName());
168                XMLUtils.startElement(contentHandler, "tag", attrs);
169                tag.getTitle().toSAX(contentHandler);
170                XMLUtils.endElement(contentHandler, "tag");
171            }
172        }
173        
174        XMLUtils.endElement(contentHandler, "tags");
175    }
176    
177    /**
178     * Get the contextual parameters for content
179     * @param content the content
180     * @return the content's contextual parameters
181     */
182    protected Map<String, Object> getContextualParameters (Content content)
183    {
184        return new HashMap<>();
185    }
186    
187    /**
188     * Sax the referencing contents
189     * @param content The content
190     * @throws SAXException if an error occurred while saxing
191     */
192    protected void _saxReferencingContents (Content content) throws SAXException
193    {
194        XMLUtils.startElement(contentHandler, "referencing-contents");
195        
196        Locale locale = content.getLanguage() != null ? new Locale(content.getLanguage()) : null;
197        for (Content referrerContent : content.getReferencingContents())
198        {
199            AttributesImpl refererAttrs = new AttributesImpl();
200            refererAttrs.addCDATAAttribute("id", referrerContent.getId());
201            refererAttrs.addCDATAAttribute("name", referrerContent.getName());
202            refererAttrs.addCDATAAttribute("title", referrerContent.getTitle(locale));
203            
204            XMLUtils.createElement(contentHandler, "referencing-content", refererAttrs);
205        }
206        
207        XMLUtils.endElement(contentHandler, "referencing-contents");
208    }
209    
210    /**
211     * SAX the content types hierarchy
212     * @param content The content type
213     * @throws SAXException if an error occurred while saxing
214     */
215    protected void _saxContentTypesHierarchy (Content content) throws SAXException
216    {
217        // Sort content types
218        TreeSet<ContentType> treeSet = new TreeSet<>(new ContentTypeComparator());
219        for (String id : content.getTypes())
220        {
221            ContentType contentType = _contentTypeEP.getExtension(id);
222            treeSet.add(contentType);
223        }
224        
225        XMLUtils.startElement(contentHandler, "content-types");
226        for (ContentType cType : treeSet)
227        {
228            _saxContentType (cType);
229        }
230        XMLUtils.endElement(contentHandler, "content-types");
231    }
232    
233    /**
234     * SAX the content types hierarchy
235     * @param content The content type
236     * @throws SAXException if an error occurred while saxing
237     */
238    protected void _saxMixinsHierarchy (Content content) throws SAXException
239    {
240        XMLUtils.startElement(contentHandler, "mixins");
241        
242        String[] contentTypes = content.getMixinTypes();
243        for (String id : contentTypes)
244        {
245            ContentType cType = _contentTypeEP.getExtension(id);
246            _saxMixin(cType);
247        }
248        
249        XMLUtils.endElement(contentHandler, "mixins");
250    }
251    
252    /**
253     * SAX the content type hierarchy
254     * @param cType The content type
255     * @throws SAXException if an error occurred while saxing
256     */
257    protected void _saxContentType (ContentType cType) throws SAXException
258    {
259        AttributesImpl attrs = new AttributesImpl();
260        
261        attrs.addCDATAAttribute("id", cType.getId());
262        
263        if (cType.getSmallIcon() != null)
264        {
265            attrs.addCDATAAttribute("icon", cType.getSmallIcon());
266        }
267        if (cType.getIconGlyph() != null)
268        {
269            attrs.addCDATAAttribute("icon-glyph", cType.getIconGlyph());
270        }
271        if (cType.getIconDecorator() != null)
272        {
273            attrs.addCDATAAttribute("icon-decorator", cType.getIconDecorator());
274        }
275        
276        XMLUtils.startElement(contentHandler, "content-type", attrs);
277        cType.getLabel().toSAX(contentHandler, "label");
278        
279        for (String id : cType.getSupertypeIds())
280        {
281            ContentType superType = _contentTypeEP.getExtension(id);
282            _saxContentType(superType);
283        }
284        XMLUtils.endElement(contentHandler, "content-type");
285    }
286    
287    /**
288     * SAX the mixin hierarchy
289     * @param mixin The mixin type
290     * @throws SAXException if an error occurred while saxing
291     */
292    protected void _saxMixin (ContentType mixin) throws SAXException
293    {
294        AttributesImpl attrs = new AttributesImpl();
295        
296        attrs.addCDATAAttribute("id", mixin.getId());
297        if (mixin.getSmallIcon() != null)
298        {
299            attrs.addCDATAAttribute("icon", mixin.getSmallIcon());
300        }
301        if (mixin.getIconGlyph() != null)
302        {
303            attrs.addCDATAAttribute("icon-glyph", mixin.getIconGlyph());
304        }
305        if (mixin.getIconDecorator() != null)
306        {
307            attrs.addCDATAAttribute("icon-decorator", mixin.getIconDecorator());
308        }
309        
310        XMLUtils.startElement(contentHandler, "mixin", attrs);
311        mixin.getLabel().toSAX(contentHandler, "label");
312        for (String id : mixin.getSupertypeIds())
313        {
314            ContentType superType = _contentTypeEP.getExtension(id);
315            _saxMixin(superType);
316        }
317        XMLUtils.endElement(contentHandler, "mixin");
318    }
319
320    class ContentTypeComparator implements Comparator<ContentType>
321    {
322        @Override
323        public int compare(ContentType c1, ContentType c2)
324        {
325            I18nizableText t1 = c1.getLabel();
326            I18nizableText t2 = c2.getLabel();
327            
328            String str1 = t1.isI18n() ? t1.getKey() : t1.getLabel();
329            String str2 = t2.isI18n() ? t2.getKey() : t2.getLabel();
330            
331            int compareTo = str1.toString().compareTo(str2.toString());
332            if (compareTo == 0)
333            {
334                // Content types have same keys but there are not equals, so do not return 0 to add it in TreeSet
335                // Indeed, in a TreeSet implementation two elements that are equal by the method compareTo are, from the standpoint of the set, equal 
336                return 1;
337            }
338            return compareTo;
339        }
340    }
341}