001/* 002 * Copyright 2010 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.List; 020import java.util.Map; 021 022import org.apache.avalon.framework.service.ServiceException; 023import org.apache.avalon.framework.service.ServiceManager; 024import org.apache.cocoon.ProcessingException; 025import org.apache.cocoon.environment.ObjectModelHelper; 026import org.apache.cocoon.environment.Request; 027import org.apache.cocoon.generation.Generator; 028import org.apache.cocoon.generation.ServiceableGenerator; 029import org.apache.cocoon.xml.AttributesImpl; 030import org.apache.cocoon.xml.XMLUtils; 031import org.apache.commons.lang.StringUtils; 032import org.xml.sax.SAXException; 033 034import org.ametys.cms.contenttype.AbstractMetadataSetElement; 035import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 036import org.ametys.cms.contenttype.ContentTypesHelper; 037import org.ametys.cms.contenttype.Fieldset; 038import org.ametys.cms.contenttype.MetadataDefinition; 039import org.ametys.cms.contenttype.MetadataDefinitionReference; 040import org.ametys.cms.contenttype.MetadataManager; 041import org.ametys.cms.contenttype.MetadataSet; 042import org.ametys.cms.contenttype.MetadataType; 043import org.ametys.cms.contenttype.RepeaterDefinition; 044import org.ametys.cms.contenttype.RichTextMetadataDefinition; 045import org.ametys.cms.contenttype.SemanticAnnotation; 046import org.ametys.cms.repository.Content; 047import org.ametys.core.right.RightManager; 048import org.ametys.core.right.RightManager.RightResult; 049import org.ametys.core.user.CurrentUserProvider; 050import org.ametys.runtime.i18n.I18nizableText; 051import org.ametys.runtime.parameter.Enumerator; 052import org.ametys.runtime.parameter.ParameterHelper; 053import org.ametys.runtime.parameter.Validator; 054 055/** 056 * {@link Generator} for rendering the structure of a metadata set 057 */ 058public class MetadataSetDefGenerator extends ServiceableGenerator 059{ 060 /** Content type extension point. */ 061 protected ContentTypeExtensionPoint _contentTypeExtensionPoint; 062 /** Metadata manager. */ 063 protected MetadataManager _metadataManager; 064 /** Helper for content types */ 065 protected ContentTypesHelper _contentTypesHelper; 066 /** Rights manager */ 067 protected RightManager _rightManager; 068 /** The current user provider */ 069 protected CurrentUserProvider _currentUserProvider; 070 /** The content helper */ 071 protected ContentHelper _contentHelper; 072 073 @Override 074 public void service(ServiceManager serviceManager) throws ServiceException 075 { 076 super.service(serviceManager); 077 _contentTypeExtensionPoint = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE); 078 _metadataManager = (MetadataManager) serviceManager.lookup(MetadataManager.ROLE); 079 _contentTypesHelper = (ContentTypesHelper) serviceManager.lookup(ContentTypesHelper.ROLE); 080 _rightManager = (RightManager) serviceManager.lookup(RightManager.ROLE); 081 _currentUserProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE); 082 _contentHelper = (ContentHelper) manager.lookup(ContentHelper.ROLE); 083 } 084 085 @Override 086 public void generate() throws IOException, SAXException, ProcessingException 087 { 088 Request request = ObjectModelHelper.getRequest(objectModel); 089 Content content = (Content) request.getAttribute(Content.class.getName()); 090 boolean isEditionMetadataSet = parameters.getParameterAsBoolean("isEditionMetadataSet", false); 091 String metadataSetName = parameters.getParameter("metadataSetName", "main"); 092 093 contentHandler.startDocument(); 094 095 AttributesImpl attrs = new AttributesImpl(); 096 attrs.addCDATAAttribute("id", content.getId()); 097 attrs.addCDATAAttribute("name", content.getName()); 098 attrs.addCDATAAttribute("title", _contentHelper.getTitle(content)); 099 if (content.getLanguage() != null) 100 { 101 attrs.addCDATAAttribute("lang", content.getLanguage()); 102 } 103 104 XMLUtils.startElement(contentHandler, "content", attrs); 105 106 AttributesImpl attrs2 = new AttributesImpl(); 107 attrs2.addCDATAAttribute("metadataSetName", metadataSetName); 108 attrs2.addCDATAAttribute("isEditionMetadataSet", Boolean.toString(isEditionMetadataSet)); 109 XMLUtils.startElement(contentHandler, "metadataSet", attrs2); 110 _saxMetadataSet(content, metadataSetName, isEditionMetadataSet); 111 XMLUtils.endElement(contentHandler, "metadataSet"); 112 113 XMLUtils.endElement(contentHandler, "content"); 114 contentHandler.endDocument(); 115 } 116 117 /** 118 * SAX metadata set structure 119 * @param content the content. 120 * @param metadataSetName The name of the metadata set to sax 121 * @param isEditionMetadataSet True to use the edit metadata set 122 * @throws SAXException if an error occurs while SAXing. 123 * @throws IOException if an error occurs. 124 * @throws ProcessingException if an error occurs. 125 */ 126 protected void _saxMetadataSet(Content content, String metadataSetName, boolean isEditionMetadataSet) throws SAXException, ProcessingException, IOException 127 { 128 MetadataSet metadataSet = null; 129 130 if (isEditionMetadataSet) 131 { 132 metadataSet = _contentTypesHelper.getMetadataSetForEdition(metadataSetName, content.getTypes(), content.getMixinTypes()); 133 } 134 else 135 { 136 metadataSet = _contentTypesHelper.getMetadataSetForView(metadataSetName, content.getTypes(), content.getMixinTypes()); 137 } 138 139 if (metadataSet == null) 140 { 141 throw new ProcessingException(String.format("Unknown metadata set '%s' of type '%s' for content type '%s'", 142 metadataSetName, isEditionMetadataSet ? "edition" : "view", StringUtils.join(content.getTypes(), ','))); 143 } 144 145 _saxMetadataSetElement(content, null, metadataSet); 146 } 147 148 /** 149 * SAX metadata set element. 150 * @param content the content. 151 * @param metadataDefinition the metadata definition. 152 * @param metadataSetElement the metadata set element. 153 * @throws SAXException if an error occurs while SAXing. 154 */ 155 protected void _saxMetadataSetElement(Content content, MetadataDefinition metadataDefinition, AbstractMetadataSetElement metadataSetElement) throws SAXException 156 { 157 for (AbstractMetadataSetElement subMetadataSetElement : metadataSetElement.getElements()) 158 { 159 if (subMetadataSetElement instanceof MetadataDefinitionReference) 160 { 161 MetadataDefinitionReference metadataDefRef = (MetadataDefinitionReference) subMetadataSetElement; 162 String metadataName = metadataDefRef.getMetadataName(); 163 MetadataDefinition metaDef = _getMetadataDefinition(content, metadataDefinition, metadataName); 164 165 if (metaDef == null) 166 { 167 throw new IllegalArgumentException("Unable to get the metadata definition for metadata with name '" + metadataName + "' (" + StringUtils.join(content.getTypes(), ',') + ")."); 168 } 169 170 if (_contentTypesHelper.canRead(content, metaDef)) 171 { 172 _saxMetadataDefinition(content, metadataDefRef, metaDef); 173 } 174 } 175 else 176 { 177 Fieldset fieldset = (Fieldset) subMetadataSetElement; 178 179 AttributesImpl attrs = new AttributesImpl(); 180 attrs.addCDATAAttribute("role", fieldset.getRole()); 181 182 XMLUtils.startElement(contentHandler, "fieldset", attrs); 183 fieldset.getLabel().toSAX(contentHandler, "label"); 184 _saxMetadataSetElement(content, metadataDefinition, fieldset); 185 XMLUtils.endElement(contentHandler, "fieldset"); 186 } 187 } 188 } 189 190 /** 191 * Retrieves a sub metadata definition from a content type or 192 * a parent metadata definition. 193 * @param content the content. 194 * @param parentMetadataDefinition the parent metadata definition. 195 * @param metadataName the metadata name. 196 * @return the metadata definition found or <code>null</code> otherwise. 197 */ 198 protected MetadataDefinition _getMetadataDefinition(Content content, MetadataDefinition parentMetadataDefinition, String metadataName) 199 { 200 MetadataDefinition metadataDefinition = null; 201 202 if (parentMetadataDefinition == null) 203 { 204 if (content != null) 205 { 206 metadataDefinition = _contentTypesHelper.getMetadataDefinition(metadataName, content.getTypes(), content.getMixinTypes()); 207 } 208 } 209 else 210 { 211 metadataDefinition = parentMetadataDefinition.getMetadataDefinition(metadataName); 212 } 213 214 return metadataDefinition; 215 } 216 217 /** 218 * Sax the metadata definition 219 * @param content The content 220 * @param metadataSetElement The metadata set element 221 * @param metaDef The metadata definition 222 * @throws SAXException if an error occurs while SAXing. 223 */ 224 protected void _saxMetadataDefinition(Content content, AbstractMetadataSetElement metadataSetElement, MetadataDefinition metaDef) throws SAXException 225 { 226 XMLUtils.startElement(contentHandler, metaDef.getName()); 227 228 XMLUtils.startElement(contentHandler, "label"); 229 I18nizableText label = metaDef.getLabel(); 230 if (label != null) 231 { 232 label.toSAX(contentHandler); 233 } 234 XMLUtils.endElement(contentHandler, "label"); 235 236 XMLUtils.startElement(contentHandler, "description"); 237 I18nizableText description = metaDef.getDescription(); 238 if (description != null) 239 { 240 description.toSAX(contentHandler); 241 } 242 XMLUtils.endElement(contentHandler, "description"); 243 244 XMLUtils.createElement(contentHandler, "type", metaDef.getType().name()); 245 String cTypeId = metaDef.getContentType(); 246 if (cTypeId != null) 247 { 248 XMLUtils.createElement(contentHandler, "contentType", cTypeId); 249 } 250 251 Validator validator = metaDef.getValidator(); 252 ParameterHelper.toSAXValidator(contentHandler, validator); 253 254 if (metaDef.getType() == MetadataType.RICH_TEXT) 255 { 256 XMLUtils.createElement(contentHandler, "editableSource", Boolean.toString(_rightManager.hasRight(_currentUserProvider.getUser(), "CORE_Rights_SourceEdit", content) == RightResult.RIGHT_ALLOW)); 257 } 258 259 String widget = metaDef.getWidget(); 260 if (widget != null) 261 { 262 XMLUtils.createElement(contentHandler, "widget", widget); 263 } 264 265 Map<String, I18nizableText> widgetParameters = metaDef.getWidgetParameters(); 266 if (widgetParameters != null && !widgetParameters.isEmpty()) 267 { 268 XMLUtils.startElement(contentHandler, "widget-params"); 269 for (String paramName : widgetParameters.keySet()) 270 { 271 XMLUtils.startElement(contentHandler, paramName); 272 widgetParameters.get(paramName).toSAX(contentHandler); 273 XMLUtils.endElement(contentHandler, paramName); 274 } 275 XMLUtils.endElement(contentHandler, "widget-params"); 276 } 277 278 279 XMLUtils.createElement(contentHandler, "multiple", String.valueOf(metaDef.isMultiple())); 280 281 Object defaultValue = metaDef.getDefaultValue(); 282 283 if (defaultValue != null) 284 { 285 XMLUtils.createElement(contentHandler, "default-value", String.valueOf(metaDef.getDefaultValue())); 286 } 287 288 Enumerator enumerator = metaDef.getEnumerator(); 289 290 if (enumerator != null) 291 { 292 _saxEnumeration(enumerator); 293 } 294 295 if (!_contentTypesHelper.canWrite(content, metaDef)) 296 { 297 XMLUtils.createElement(contentHandler, "can-not-write", "true"); 298 } 299 300 if (metaDef.getType() == MetadataType.COMPOSITE) 301 { 302 _saxCompositeDefinition(content, metadataSetElement, metaDef); 303 } 304 305 if (metaDef instanceof RichTextMetadataDefinition) 306 { 307 _saxAnnotableDefinition(content, metadataSetElement, (RichTextMetadataDefinition) metaDef); 308 } 309 310 XMLUtils.endElement(contentHandler, metaDef.getName()); 311 } 312 313 private void _saxEnumeration(Enumerator enumerator) throws SAXException 314 { 315 XMLUtils.startElement(contentHandler, "enumeration"); 316 317 try 318 { 319 for (Map.Entry<Object, I18nizableText> entry : enumerator.getEntries().entrySet()) 320 { 321 String valueAsString = ParameterHelper.valueToString(entry.getKey()); 322 I18nizableText entryLabel = entry.getValue(); 323 AttributesImpl attrs = new AttributesImpl(); 324 attrs.addCDATAAttribute("value", valueAsString); 325 XMLUtils.startElement(contentHandler, "option", attrs); 326 327 if (entryLabel != null) 328 { 329 entryLabel.toSAX(contentHandler); 330 } 331 else 332 { 333 XMLUtils.data(contentHandler, valueAsString); 334 } 335 336 XMLUtils.endElement(contentHandler, "option"); 337 } 338 } 339 catch (Exception e) 340 { 341 throw new SAXException("Unable to enumerate entries with enumerator: " + enumerator, e); 342 } 343 344 XMLUtils.endElement(contentHandler, "enumeration"); 345 } 346 347 /** 348 * SAX the annotations of a definition 349 * @param content the content. 350 * @param metadataSetElement the metadata set element. 351 * @param metaDef the metadata definition. 352 * @throws SAXException if an error occurs while SAXing. 353 */ 354 private void _saxAnnotableDefinition(Content content, AbstractMetadataSetElement metadataSetElement, RichTextMetadataDefinition metaDef) throws SAXException 355 { 356 List<SemanticAnnotation> annotations = metaDef.getSemanticAnnotations(); 357 if (annotations != null && annotations.size() > 0) 358 { 359 XMLUtils.startElement(contentHandler, "annotations"); 360 for (SemanticAnnotation annotation : annotations) 361 { 362 AttributesImpl attrs = new AttributesImpl(); 363 attrs.addCDATAAttribute("name", annotation.getId()); 364 365 XMLUtils.startElement(contentHandler, "annotation", attrs); 366 367 I18nizableText label = annotation.getLabel(); 368 if (label != null) 369 { 370 label.toSAX(contentHandler, "label"); 371 } 372 373 I18nizableText description = annotation.getDescription(); 374 if (description != null) 375 { 376 description.toSAX(contentHandler, "description"); 377 } 378 379 XMLUtils.endElement(contentHandler, "annotation"); 380 } 381 XMLUtils.endElement(contentHandler, "annotations"); 382 } 383 } 384 385 /** 386 * Sax a composite definition 387 * @param content the content. 388 * @param metadataSetElement the metadata set element. 389 * @param metaDef the metadata definition. 390 * @throws SAXException if an error occurs while SAXing. 391 */ 392 private void _saxCompositeDefinition(Content content, AbstractMetadataSetElement metadataSetElement, MetadataDefinition metaDef) throws SAXException 393 { 394 if (metaDef instanceof RepeaterDefinition) 395 { 396 if (!_contentTypesHelper.canRead(content, metaDef)) 397 { 398 return; 399 } 400 401 AttributesImpl repeaterAttrs = new AttributesImpl(); 402 RepeaterDefinition repeaterDef = (RepeaterDefinition) metaDef; 403 I18nizableText addLabel = repeaterDef.getAddLabel(); 404 I18nizableText delLabel = repeaterDef.getDeleteLabel(); 405 String headerLabel = repeaterDef.getHeaderLabel(); 406 int maxSize = repeaterDef.getMaxSize(); 407 408 repeaterAttrs.addCDATAAttribute("initial-size", String.valueOf(repeaterDef.getInitialSize())); 409 repeaterAttrs.addCDATAAttribute("min-size", String.valueOf(repeaterDef.getMinSize())); 410 411 if (maxSize >= 0) 412 { 413 repeaterAttrs.addCDATAAttribute("max-size", String.valueOf(maxSize)); 414 } 415 416 XMLUtils.startElement(contentHandler, "repeater", repeaterAttrs); 417 418 if (addLabel != null) 419 { 420 addLabel.toSAX(contentHandler, "add-label"); 421 } 422 423 if (delLabel != null) 424 { 425 delLabel.toSAX(contentHandler, "del-label"); 426 } 427 428 if (StringUtils.isNotEmpty(headerLabel)) 429 { 430 XMLUtils.createElement(contentHandler, "header-label", headerLabel); 431 } 432 433 if (!_contentTypesHelper.canWrite(content, metaDef)) 434 { 435 XMLUtils.createElement(contentHandler, "can-not-write", "true"); 436 } 437 } 438 439 XMLUtils.startElement(contentHandler, "composition"); 440 _saxMetadataSetElement(content, metaDef, metadataSetElement); 441 XMLUtils.endElement(contentHandler, "composition"); 442 443 if (metaDef instanceof RepeaterDefinition) 444 { 445 XMLUtils.endElement(contentHandler, "repeater"); 446 } 447 } 448}