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