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