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