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