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