001/* 002 * Copyright 2011 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.odf.enumeration; 017 018import java.util.HashMap; 019import java.util.HashSet; 020import java.util.LinkedHashMap; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024import java.util.stream.Collectors; 025 026import org.apache.avalon.framework.component.Component; 027import org.apache.avalon.framework.logger.AbstractLogEnabled; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.avalon.framework.service.Serviceable; 031import org.apache.cocoon.xml.AttributesImpl; 032import org.apache.cocoon.xml.XMLUtils; 033import org.apache.commons.lang3.StringUtils; 034import org.xml.sax.ContentHandler; 035import org.xml.sax.SAXException; 036 037import org.ametys.cms.contenttype.AbstractMetadataSetElement; 038import org.ametys.cms.contenttype.ContentConstants; 039import org.ametys.cms.contenttype.ContentType; 040import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 041import org.ametys.cms.contenttype.ContentTypesHelper; 042import org.ametys.cms.contenttype.MetadataDefinition; 043import org.ametys.cms.contenttype.MetadataDefinitionHolder; 044import org.ametys.cms.contenttype.MetadataDefinitionReference; 045import org.ametys.cms.contenttype.MetadataSet; 046import org.ametys.cms.contenttype.MetadataType; 047import org.ametys.cms.contenttype.RepeaterDefinition; 048import org.ametys.cms.repository.Content; 049import org.ametys.cms.repository.ContentQueryHelper; 050import org.ametys.cms.repository.ContentTypeExpression; 051import org.ametys.core.ui.Callable; 052import org.ametys.plugins.repository.AmetysObjectIterable; 053import org.ametys.plugins.repository.AmetysObjectIterator; 054import org.ametys.plugins.repository.AmetysObjectResolver; 055import org.ametys.plugins.repository.UnknownAmetysObjectException; 056import org.ametys.plugins.repository.query.expression.AndExpression; 057import org.ametys.plugins.repository.query.expression.Expression.Operator; 058import org.ametys.plugins.repository.query.expression.StringExpression; 059import org.ametys.runtime.config.Config; 060 061/** 062 * This component handles ODF reference tables 063 * 064 */ 065public class OdfReferenceTableHelper extends AbstractLogEnabled implements Component, Serviceable 066{ 067 /** Avalon Role */ 068 public static final String ROLE = OdfReferenceTableHelper.class.getName(); 069 070 /** Abstract table ref */ 071 public static final String ABSTRACT_TABLE_REF = "odf-enumeration.AbstractTableRef"; 072 /** Domain */ 073 public static final String DOMAIN = "odf-enumeration.Domain"; 074 /** Degree */ 075 public static final String DEGREE = "odf-enumeration.Degree"; 076 /** Level */ 077 public static final String LEVEL = "odf-enumeration.Level"; 078 /** Program type. */ 079 public static final String PROGRAM_TYPE = "odf-enumeration.ProgramType"; 080 /** Form of teaching */ 081 public static final String FORMOFTEACHING_METHOD = "odf-enumeration.FormofteachingMethod"; 082 /** Orgnization of teaching */ 083 public static final String FORMOFTEACHING_ORG = "odf-enumeration.FormofteachingOrg"; 084 /** Teaching method */ 085 public static final String TEACHING_ACTIVITY = "odf-enumeration.TeachingActivity"; 086 /** Type of training course */ 087 public static final String INTERNSHIP = "odf-enumeration.Internship"; 088 /** Distance learning modalities */ 089 public static final String DISTANCE_LEARNING_MODALITIES = "odf-enumeration.DistanceLearningModalities"; 090 /** Place */ 091 public static final String PLACE = "odf-enumeration.Place"; 092 /** Teaching term. */ 093 public static final String TEACHING_TERM = "odf-enumeration.TeachingTerm"; 094 /** Time slot */ 095 public static final String TIME_SLOT = "odf-enumeration.TimeSlot"; 096 /** Code ROME */ 097 public static final String CODE_ROME = "odf-enumeration.CodeRome"; 098 /** Code ERASMUS */ 099 public static final String CODE_ERASMUS = "odf-enumeration.CodeErasmus"; 100 /** Code DGESIP */ 101 public static final String CODE_DGESIP = "odf-enumeration.CodeDgesip"; 102 /** Code SISE */ 103 public static final String CODE_SISE = "odf-enumeration.CodeSise"; 104 /** Code Cite97 */ 105 public static final String CODE_CITE97 = "odf-enumeration.CodeCite97"; 106 /** Code FAP */ 107 public static final String CODE_FAP = "odf-enumeration.CodeFap"; 108 /** Code NSF */ 109 public static final String CODE_NSF = "odf-enumeration.CodeNsf"; 110 /** RNCP level */ 111 public static final String RNCP_LEVEL = "odf-enumeration.RncpLevel"; 112 /** Join orgunit*/ 113 public static final String JOIN_ORGUNIT = "odf-enumeration.JoinOrgunit"; 114 /** Mention licence */ 115 public static final String ABSTRACT_MENTION = "odf-enumeration.Mention"; 116 /** Mention licence */ 117 public static final String MENTION_LICENCE = "odf-enumeration.MentionLicence"; 118 /** Mention licence pro */ 119 public static final String MENTION_LICENCEPRO = "odf-enumeration.MentionLicencepro"; 120 /** Mention master */ 121 public static final String MENTION_MASTER = "odf-enumeration.MentionMaster"; 122 /** Abstract table ref for category */ 123 public static final String ABSTRACT_TABLE_REF_CATEGORY = "odf-enumeration.AbstractTableRefCategory"; 124 /** Apprenticeship contract */ 125 public static final String APPRENTICESHIP_CONTRACT = "odf-enumeration.ApprenticeshipContract"; 126 /** Available certification */ 127 public static final String AVAILABLE_CERTIFICATION = "odf-enumeration.AvailableCertification"; 128 /** Campus */ 129 public static final String CAMPUS = "odf-enumeration.Campus"; 130 /** Category for code Erasmus */ 131 public static final String CODE_ERASMUS_CATEGORY = "odf-enumeration.CodeErasmusCategory"; 132 /** Category for code FAP */ 133 public static final String CODE_FAP_CATEGORY = "odf-enumeration.CodeFapCategory"; 134 /** Nature of container */ 135 public static final String CONTAINER_NATURE = "odf-enumeration.ContainerNature"; 136 /** Nature of course */ 137 public static final String COURSE_NATURE = "odf-enumeration.CourseNature"; 138 /** Discipline */ 139 public static final String DISCIPLINE = "odf-enumeration.Discipline"; 140 /** Duration */ 141 public static final String DURATION = "odf-enumeration.Duration"; 142 /** ECTS */ 143 public static final String ECTS = "odf-enumeration.Ects"; 144 /** Foreign place */ 145 public static final String FOREIGN_PLACE = "odf-enumeration.ForeignPlace"; 146 /** International education */ 147 public static final String INTERNATIONAL_EDUCATION = "odf-enumeration.InternationalEducation"; 148 /** Language */ 149 public static final String LANGUAGE = "odf-enumeration.Language"; 150 /** OrgUnit type */ 151 public static final String ORGUNIT_TYPE = "odf-enumeration.OrgUnitType"; 152 /** Period */ 153 public static final String PERIOD = "odf-enumeration.Period"; 154 /** Person role */ 155 public static final String PERSON_ROLE = "odf-enumeration.PersonRole"; 156 /** Program field */ 157 public static final String PROGRAM_FIELD = "odf-enumeration.ProgramField"; 158 /** Sectors */ 159 public static final String SECTORS = "odf-enumeration.Sectors"; 160 161 private AmetysObjectResolver _resolver; 162 163 private ContentTypeExtensionPoint _cTypeEP; 164 165 private ContentTypesHelper _cTypeHelper; 166 167 @Override 168 public void service(ServiceManager manager) throws ServiceException 169 { 170 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 171 _cTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE); 172 _cTypeHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE); 173 } 174 175 /** 176 * Determines if the content type is a ODF table reference 177 * @param cTypeId The id of content type 178 * @return true if the content type is a ODF table reference 179 */ 180 public boolean isTableReference(String cTypeId) 181 { 182 return _cTypeHelper.getAncestors(cTypeId).contains(ABSTRACT_TABLE_REF); 183 } 184 185 /** 186 * Determines if the content is an entry of a ODF table reference 187 * @param content The content 188 * @return <code>true</code> if the content is an entry of a ODF table reference 189 */ 190 public boolean isTableReferenceEntry(Content content) 191 { 192 String[] cTypeIds = content.getTypes(); 193 for (String cTypeId : cTypeIds) 194 { 195 if (isTableReference(cTypeId)) 196 { 197 return true; 198 } 199 } 200 return false; 201 } 202 203 /** 204 * Get the id of table references 205 * @return The content type's id 206 */ 207 public Set<String> getTableReferenceIds() 208 { 209 Set<String> tableRefIds = new HashSet<>(); 210 211 for (String cTypeId : _cTypeEP.getExtensionsIds()) 212 { 213 if (_cTypeHelper.getAncestors(cTypeId).contains(ABSTRACT_TABLE_REF)) 214 { 215 tableRefIds.add(cTypeId); 216 } 217 } 218 return tableRefIds; 219 } 220 221 /** 222 * Get the metadata definitions for table references for a given content type 223 * @param cTypeId The id of content type 224 * @return The metadata definitions for table references 225 */ 226 public Map<String, MetadataDefinition> getTableRefMetadataDefinition(String cTypeId) 227 { 228 ContentType cType = _cTypeEP.getExtension(cTypeId); 229 return _getTableRefMetadata(cType, ""); 230 } 231 232 /** 233 * Get the metadata definitions for table references for a given content type and a metadata set 234 * @param cTypeId The id of content type 235 * @param medatataSetName the name of the metadata set. Cannot be null. 236 * @param isEditionMetadataSet true if the metadata set is for edition. 237 * @return The metadata definitions for table references 238 */ 239 public Map<String, MetadataDefinition> getTableRefMetadataDefinition(String cTypeId, String medatataSetName, boolean isEditionMetadataSet) 240 { 241 ContentType cType = _cTypeEP.getExtension(cTypeId); 242 243 MetadataSet metadataSet = isEditionMetadataSet ? cType.getMetadataSetForEdition(medatataSetName) : cType.getMetadataSetForView(medatataSetName); 244 return _getTableRefMetadata(metadataSet, cType, ""); 245 } 246 247 private Map<String, MetadataDefinition> _getTableRefMetadata(MetadataDefinitionHolder parentMetaDef, String prefix) 248 { 249 Map<String, MetadataDefinition> tableRefMetadata = new LinkedHashMap<>(); 250 251 Set<String> metadataNames = parentMetaDef.getMetadataNames(); 252 for (String metadataName : metadataNames) 253 { 254 MetadataDefinition metaDef = parentMetaDef.getMetadataDefinition(metadataName); 255 MetadataType type = metaDef.getType(); 256 257 switch (type) 258 { 259 case COMPOSITE: 260 if (!(metaDef instanceof RepeaterDefinition)) 261 { 262 // enumerated metadata in repeaters are not supported 263 tableRefMetadata.putAll(_getTableRefMetadata(metaDef, prefix + metadataName + ContentConstants.METADATA_PATH_SEPARATOR)); 264 } 265 break; 266 267 case CONTENT: 268 if (isTableReference(metaDef.getContentType())) 269 { 270 tableRefMetadata.put(prefix + metadataName, metaDef); 271 } 272 break; 273 274 default: 275 break; 276 } 277 } 278 279 return tableRefMetadata; 280 } 281 282 private Map<String, MetadataDefinition> _getTableRefMetadata (AbstractMetadataSetElement element, MetadataDefinitionHolder parentMetaDef, String prefix) 283 { 284 Map<String, MetadataDefinition> tableRefMetadata = new LinkedHashMap<>(); 285 286 for (AbstractMetadataSetElement subElement : element.getElements()) 287 { 288 if (subElement instanceof MetadataDefinitionReference) 289 { 290 MetadataDefinitionReference metadataDefRef = (MetadataDefinitionReference) subElement; 291 String metadataName = metadataDefRef.getMetadataName(); 292 MetadataDefinition metaDef = parentMetaDef.getMetadataDefinition(metadataName); 293 294 MetadataType type = metaDef.getType(); 295 296 switch (type) 297 { 298 case COMPOSITE: 299 if (!(metaDef instanceof RepeaterDefinition)) 300 { 301 // table ref metadata in repeaters are not supported 302 tableRefMetadata.putAll(_getTableRefMetadata(subElement, metaDef, prefix + metadataName + ContentConstants.METADATA_PATH_SEPARATOR)); 303 } 304 break; 305 306 case CONTENT: 307 if (isTableReference(metaDef.getContentType())) 308 { 309 tableRefMetadata.put(prefix + metadataName, metaDef); 310 } 311 break; 312 313 default: 314 break; 315 } 316 } 317 } 318 return tableRefMetadata; 319 } 320 321 /** 322 * Get the content type for mention for this degree 323 * @param degreeIds The ids of degrees 324 * @return A map with the id of content type or null if there is no mention for this degree 325 */ 326 @Callable 327 public Map<String, String> getMentionContentTypes(List<String> degreeIds) 328 { 329 Map<String, String> mentionTypes = new HashMap<>(); 330 331 for (String degreeId : degreeIds) 332 { 333 try 334 { 335 Content content = _resolver.resolveById(degreeId); 336 OdfReferenceTableEntry entry = new OdfReferenceTableEntry(content); 337 338 String degreeCode = entry.getCode(); 339 340 if (degreeCode.equals(Config.getInstance().getValueAsString("odf.programs.degree.license"))) 341 { 342 mentionTypes.put(degreeId, OdfReferenceTableHelper.MENTION_LICENCE); 343 } 344 else if (degreeCode.equals(Config.getInstance().getValueAsString("odf.programs.degree.licensepro"))) 345 { 346 mentionTypes.put(degreeId, OdfReferenceTableHelper.MENTION_LICENCEPRO); 347 } 348 else if (degreeCode.equals(Config.getInstance().getValueAsString("odf.programs.degree.master"))) 349 { 350 mentionTypes.put(degreeId, OdfReferenceTableHelper.MENTION_MASTER); 351 } 352 } 353 catch (UnknownAmetysObjectException e) 354 { 355 mentionTypes.put(degreeId, null); 356 } 357 } 358 return mentionTypes; 359 } 360 361 /** 362 * Get the CDM-fr value associated with the given code 363 * @param tableRefIf The id of content type 364 * @param code The code 365 * @return The CDM-fr value or empty string if not found 366 */ 367 public String getCDMfrValue (String tableRefIf, String code) 368 { 369 ContentTypeExpression cTypeExpr = new ContentTypeExpression(Operator.EQ, tableRefIf); 370 StringExpression codeExpr = new StringExpression(OdfReferenceTableEntry.METADATA_CODE, Operator.EQ, code); 371 372 String xpathQuery = ContentQueryHelper.getContentXPathQuery(new AndExpression(cTypeExpr, codeExpr)); 373 AmetysObjectIterable<Content> contents = _resolver.query(xpathQuery); 374 AmetysObjectIterator<Content> it = contents.iterator(); 375 376 if (it.hasNext()) 377 { 378 OdfReferenceTableEntry entry = new OdfReferenceTableEntry(it.next()); 379 return entry.getCdmValue(); 380 } 381 382 return ""; 383 } 384 385 /** 386 * Get all items of an enumeration and their label 387 * @param tableRefId The id of content type 388 * @return items of enumeration 389 */ 390 public List<OdfReferenceTableEntry> getItems (String tableRefId) 391 { 392 ContentTypeExpression cTypeExpr = new ContentTypeExpression(Operator.EQ, tableRefId); 393 394 String xpathQuery = ContentQueryHelper.getContentXPathQuery(cTypeExpr); 395 AmetysObjectIterable<Content> contents = _resolver.query(xpathQuery); 396 397 return contents.stream().map(c -> new OdfReferenceTableEntry(c)).collect(Collectors.toList()); 398 } 399 400 /** 401 * Returns the label of an reference table entry 402 * @param tableRefId The id of content type 403 * @param contentId The content id 404 * @param lang The requested language of label 405 * @return the item label or <code>null</code> if not found 406 */ 407 public String getItemLabel (String tableRefId, String contentId, String lang) 408 { 409 try 410 { 411 Content content = _resolver.resolveById(contentId); 412 OdfReferenceTableEntry entry = new OdfReferenceTableEntry(content); 413 return entry.getLabel(lang); 414 } 415 catch (UnknownAmetysObjectException e) 416 { 417 return null; 418 } 419 } 420 421 /** 422 * Returns the CMD value of an reference table entry 423 * @param tableRefId The id of content type 424 * @param contentId The content id 425 * @param returnCodeIfEmpty <code>true</code> to return the code if CDM-fr value is empty 426 * @return the CDM-fr value or empty value if not found 427 */ 428 public String getItemCDMfrValue (String tableRefId, String contentId, boolean returnCodeIfEmpty) 429 { 430 if (StringUtils.isEmpty(contentId)) 431 { 432 return ""; 433 } 434 435 Content content = _resolver.resolveById(contentId); 436 OdfReferenceTableEntry entry = new OdfReferenceTableEntry(content); 437 438 String cdmValue = entry.getCdmValue(); 439 440 if (StringUtils.isEmpty(cdmValue) && returnCodeIfEmpty) 441 { 442 return entry.getCode(); 443 } 444 return cdmValue; 445 } 446 447 /** 448 * Returns the code of an reference table entry from its CDM value 449 * @param tableRefId The id of content type 450 * @param contentId The id of content 451 * @return the code or empty value if not found 452 */ 453 public String getItemCode (String tableRefId, String contentId) 454 { 455 if (StringUtils.isEmpty(contentId)) 456 { 457 return ""; 458 } 459 460 try 461 { 462 Content content = _resolver.resolveById(contentId); 463 OdfReferenceTableEntry entry = new OdfReferenceTableEntry(content); 464 return entry.getCode(); 465 } 466 catch (UnknownAmetysObjectException e) 467 { 468 return ""; 469 } 470 } 471 472 /** 473 * Returns the code of an reference table entry from its CDM value 474 * @param tableRefId The id of content type 475 * @param cdmValue The CDM-fr value 476 * @return the code or <code>null</code> if not found 477 */ 478 public String getItemCodeFromCDM (String tableRefId, String cdmValue) 479 { 480 ContentTypeExpression cTypeExpr = new ContentTypeExpression(Operator.EQ, tableRefId); 481 StringExpression codeExpr = new StringExpression(OdfReferenceTableEntry.METADATA_CDM_VALUE, Operator.EQ, cdmValue); 482 483 String xpathQuery = ContentQueryHelper.getContentXPathQuery(new AndExpression(cTypeExpr, codeExpr)); 484 AmetysObjectIterable<Content> contents = _resolver.query(xpathQuery); 485 AmetysObjectIterator<Content> it = contents.iterator(); 486 487 if (it.hasNext()) 488 { 489 OdfReferenceTableEntry entry = new OdfReferenceTableEntry(it.next()); 490 return entry.getCode(); 491 } 492 return null; 493 } 494 495 /** 496 * Returns the entry of an reference table entry from its cdmValue 497 * @param tableRefId The id of content type 498 * @param cdmValue The CDM-fr value 499 * @return the entry or <code>null</code> if not found 500 */ 501 public OdfReferenceTableEntry getItemFromCDM(String tableRefId, String cdmValue) 502 { 503 ContentTypeExpression cTypeExpr = new ContentTypeExpression(Operator.EQ, tableRefId); 504 StringExpression codeExpr = new StringExpression(OdfReferenceTableEntry.METADATA_CDM_VALUE, Operator.EQ, cdmValue); 505 506 String xpathQuery = ContentQueryHelper.getContentXPathQuery(new AndExpression(cTypeExpr, codeExpr)); 507 508 return _resolver.<Content>query(xpathQuery).stream() 509 .findFirst() 510 .map(OdfReferenceTableEntry::new) 511 .orElse(null); 512 } 513 514 /** 515 * Returns the entry of an reference table entry from its code 516 * @param tableRefId The id of content type 517 * @param code The code 518 * @return the entry or <code>null</code> if not found 519 */ 520 public OdfReferenceTableEntry getItemFromCode(String tableRefId, String code) 521 { 522 ContentTypeExpression cTypeExpr = new ContentTypeExpression(Operator.EQ, tableRefId); 523 StringExpression codeExpr = new StringExpression(OdfReferenceTableEntry.METADATA_CODE, Operator.EQ, code); 524 525 String xpathQuery = ContentQueryHelper.getContentXPathQuery(new AndExpression(cTypeExpr, codeExpr)); 526 527 return _resolver.<Content>query(xpathQuery).stream() 528 .findFirst() 529 .map(OdfReferenceTableEntry::new) 530 .orElse(null); 531 } 532 533 /** 534 * SAX items of a reference table 535 * @param contentHandler The content handler to sax into 536 * @param tableRefId The id of reference table 537 * @throws SAXException if an error occurred while saxing 538 */ 539 public void saxItems (ContentHandler contentHandler, String tableRefId) throws SAXException 540 { 541 _saxItems(contentHandler, new AttributesImpl(), tableRefId); 542 } 543 544 /** 545 * SAX items of a reference table 546 * @param contentHandler the content handler to sax into 547 * @param metaDef the metadata definition 548 * @throws SAXException if an error occurs while saxing 549 */ 550 public void saxItems (ContentHandler contentHandler, MetadataDefinition metaDef) throws SAXException 551 { 552 if (metaDef.getType() == MetadataType.CONTENT) 553 { 554 String cTypeId = metaDef.getContentType(); 555 if (cTypeId.startsWith("odf-enumeration.")) 556 { 557 AttributesImpl attrs = new AttributesImpl(); 558 attrs.addCDATAAttribute("metadataName", metaDef.getName()); 559 attrs.addCDATAAttribute("metadataPath", metaDef.getId()); 560 561 _saxItems(contentHandler, attrs, cTypeId); 562 } 563 } 564 } 565 566 private void _saxItems (ContentHandler contentHandler, AttributesImpl rootAttrs, String tableRefId) throws SAXException 567 { 568 rootAttrs.addCDATAAttribute("contentTypeId", tableRefId); 569 XMLUtils.startElement(contentHandler, "items", rootAttrs); 570 571 List<OdfReferenceTableEntry> entries = getItems(tableRefId); 572 for (OdfReferenceTableEntry entry : entries) 573 { 574 AttributesImpl valueAttrs = new AttributesImpl(); 575 valueAttrs.addCDATAAttribute("id", entry.getId()); 576 valueAttrs.addCDATAAttribute("code", entry.getCode()); 577 valueAttrs.addCDATAAttribute("cdmValue", entry.getCdmValue()); 578 579 String lang = Config.getInstance().getValueAsString("odf.programs.lang"); 580 XMLUtils.createElement(contentHandler, "item", valueAttrs, entry.getLabel(lang)); 581 } 582 583 XMLUtils.endElement(contentHandler, "items"); 584 } 585}