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}