001/* 002 * Copyright 2018 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.plugins.thesaurus; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025 026import org.apache.avalon.framework.component.Component; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.avalon.framework.service.Serviceable; 030import org.apache.commons.collections.ListUtils; 031import org.apache.commons.lang3.ArrayUtils; 032import org.apache.commons.lang3.StringUtils; 033 034import org.ametys.cms.FilterNameHelper; 035import org.ametys.cms.ObservationConstants; 036import org.ametys.cms.contenttype.ContentAttributeDefinition; 037import org.ametys.cms.contenttype.ContentType; 038import org.ametys.cms.contenttype.ContentTypeDefinition; 039import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 040import org.ametys.cms.contenttype.ContentTypesHelper; 041import org.ametys.cms.contenttype.EditContentTypeException; 042import org.ametys.cms.contenttype.EditContentTypeHelper; 043import org.ametys.cms.contenttype.RemoveContentTypeException; 044import org.ametys.cms.data.type.ModelItemTypeConstants; 045import org.ametys.cms.repository.Content; 046import org.ametys.cms.repository.ContentAttributeTypeExtensionPoint; 047import org.ametys.cms.repository.ContentDAO; 048import org.ametys.cms.repository.ContentQueryHelper; 049import org.ametys.cms.repository.ContentTypeExpression; 050import org.ametys.core.observation.Event; 051import org.ametys.core.observation.ObservationManager; 052import org.ametys.core.right.RightManager; 053import org.ametys.core.right.RightManager.RightResult; 054import org.ametys.core.ui.Callable; 055import org.ametys.core.user.CurrentUserProvider; 056import org.ametys.plugins.repository.AmetysObjectIterable; 057import org.ametys.plugins.repository.AmetysObjectResolver; 058import org.ametys.plugins.repository.AmetysRepositoryException; 059import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 060import org.ametys.plugins.repository.RemovableAmetysObject; 061import org.ametys.plugins.repository.RepositoryConstants; 062import org.ametys.plugins.repository.query.expression.AndExpression; 063import org.ametys.plugins.repository.query.expression.Expression; 064import org.ametys.plugins.repository.query.expression.Expression.Operator; 065import org.ametys.plugins.repository.query.expression.MetadataExpression; 066import org.ametys.plugins.repository.query.expression.NotExpression; 067import org.ametys.plugins.repository.query.expression.StringExpression; 068import org.ametys.runtime.i18n.I18nizableText; 069import org.ametys.runtime.model.ModelItem; 070import org.ametys.runtime.model.type.ModelItemType; 071import org.ametys.runtime.plugin.component.AbstractLogEnabled; 072import org.ametys.runtime.plugin.component.PluginAware; 073 074/** 075 * DAO for manipulating thesaurus 076 * 077 */ 078public class ThesaurusDAO extends AbstractLogEnabled implements Serviceable, Component, PluginAware 079{ 080 /** The Avalon role */ 081 public static final String ROLE = ThesaurusDAO.class.getName(); 082 083 /** Metadata for related terms */ 084 public static final String MICROTHESAURUS_CONTENT_TYPE_PREFIX = "content-type.org.ametys.plugins.thesaurus.Content.term."; 085 086 /** Id of all microthesaurus super content type*/ 087 public static final String MICROTHESAURUS_ABSTRACT_CONTENT_TYPE = "org.ametys.plugins.thesaurus.Content.term"; 088 089 /** Metadata for related terms */ 090 public static final String METADATA_RELATED_TERMS = "relatedTerms"; 091 /** Metadata for specific terms */ 092 public static final String METADATA_SPECIFIC_TERMS = "specificTerms"; 093 /** Metadata for generic term */ 094 public static final String METADATA_GENERIC_TERM = "genericTerm"; 095 /** Metadata for synonyms */ 096 public static final String METADATA_SYNONYMS = "synonyms"; 097 /** Metadata for applicationNote */ 098 public static final String METADATA_APPLICATION_NOTE = "applicationNote"; 099 /** Metadata for explanatoryNote */ 100 public static final String METADATA_EXPLANATORY_NOTE = "explanatoryNote"; 101 102 private static final String __PLUGIN_NODE_NAME = "thesaurus"; 103 private static final String __ROOT_THESAURII_NODETYPE = RepositoryConstants.NAMESPACE_PREFIX + ":thesaurii"; 104 105 /** Ametys resolver */ 106 protected AmetysObjectResolver _resolver; 107 /** The right manager */ 108 protected RightManager _rightManager; 109 /** The current user provider */ 110 protected CurrentUserProvider _currentUserProvider; 111 /** The extension point for content types */ 112 protected ContentTypeExtensionPoint _contentTypeEP; 113 /** The content types helper */ 114 protected ContentTypesHelper _contentTypesHelper; 115 /** The extension point for available attribute types */ 116 protected ContentAttributeTypeExtensionPoint _contentAttributeTypeExtensionPoint; 117 /** The contentDAO */ 118 protected ContentDAO _contentDAO; 119 /** The EditContentTypeHelper */ 120 protected EditContentTypeHelper _editContentTypeHelper; 121 /** Ametys observation manger */ 122 protected ObservationManager _observationManager; 123 124 private String _pluginName; 125 126 @Override 127 public void service(ServiceManager manager) throws ServiceException 128 { 129 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 130 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 131 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 132 _contentTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE); 133 _contentTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE); 134 _contentAttributeTypeExtensionPoint = (ContentAttributeTypeExtensionPoint) manager.lookup(ContentAttributeTypeExtensionPoint.ROLE); 135 _contentDAO = (ContentDAO) manager.lookup(ContentDAO.ROLE); 136 _editContentTypeHelper = (EditContentTypeHelper) manager.lookup(EditContentTypeHelper.ROLE); 137 _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE); 138 } 139 140 public void setPluginInfo(String pluginName, String featureName, String id) 141 { 142 _pluginName = pluginName; 143 } 144 145 /** 146 * Get the list of thesaurus 147 * @return the thesaurus 148 */ 149 public AmetysObjectIterable<Thesaurus> getThesaurii() 150 { 151 ModifiableTraversableAmetysObject rootNode = getRootNode(); 152 return rootNode.getChildren(); 153 } 154 155 /** 156 * Get microthesaurii of a thesaurus 157 * @param thesaurusId The id of thesaurus 158 * @return the contentType ids of microthesaurii 159 */ 160 @Callable 161 public List<String> getMicrothesaurii (String thesaurusId) 162 { 163 Thesaurus thesaurus = _resolver.resolveById(thesaurusId); 164 return thesaurus.getMicrothesaurii(); 165 } 166 167 /** 168 * Get the list of ids of all the microthesaurii 169 * @return the list if microthesaurii ids 170 */ 171 @Callable 172 public List<String> getMicrothesaurii() 173 { 174 List<String> microthesauriiList = new ArrayList<>(); 175 AmetysObjectIterable<Thesaurus> thesaurii = getThesaurii(); 176 for (Thesaurus thesaurus: thesaurii) 177 { 178 List<String> microthesaurii = getMicrothesaurii(thesaurus.getId()); 179 microthesauriiList.addAll(microthesaurii); 180 } 181 182 return microthesauriiList; 183 } 184 185 /** 186 * Get the parent thesaurus 187 * @param microThesaurus The microthesaurus 188 * @return the parent thesaurus 189 */ 190 public Thesaurus getParentThesaurus (ContentType microThesaurus) 191 { 192 AmetysObjectIterable<Thesaurus> thesaurii = getThesaurii(); 193 for (Thesaurus thesaurus : thesaurii) 194 { 195 if (thesaurus.getMicrothesaurii().contains(microThesaurus.getId())) 196 { 197 return thesaurus; 198 } 199 } 200 201 return null; 202 } 203 204 /** 205 * Get the contentTypeDefinition for a microthesaurus 206 * @param cTypeId The id of content type for this microthesaurus 207 * @param label The label of the microthesaurus 208 * @param parentThesaurus The parent thesaurus 209 * @return The ContentTypeDefinition of the reference table content type 210 */ 211 private ContentTypeDefinition _getMicrothesaurusContentTypeDefinition(String cTypeId, String label, Thesaurus parentThesaurus) 212 { 213 ContentTypeDefinition cTypeDef = new ContentTypeDefinition(cTypeId); 214 215 cTypeDef.setPluginName(_pluginName); 216 cTypeDef.setLabel(new I18nizableText(label)); 217 cTypeDef.setDescription(new I18nizableText(label)); 218 cTypeDef.setDefaultTitle(new I18nizableText("plugin." + _pluginName, "PLUGINS_THESAURUS_CONTENT_TYPE_TERM_DEFAULT_TITLE")); 219 cTypeDef.setCategory(new I18nizableText(parentThesaurus.getLabel())); 220 cTypeDef.setIconGlyph("ametysicon-letter-a"); 221 cTypeDef.setSupertypeIds(new String[] {MICROTHESAURUS_ABSTRACT_CONTENT_TYPE}); 222 223 // Set tags 224 Set<String> tags = new HashSet<>(); 225 tags.add("private"); 226 tags.add("reference-table"); 227 tags.add("allow-candidates"); 228 cTypeDef.setTags(tags); 229 230 // Additional model items 231 List<ModelItem> modelItems = _getModelItems(cTypeId); 232 cTypeDef.setModelItems(modelItems); 233 cTypeDef.setParentRef(METADATA_GENERIC_TERM); 234 235 // Do not override metadatasets from the supertypTerm 236 cTypeDef.setViews(ListUtils.EMPTY_LIST); 237 238 return cTypeDef; 239 } 240 241 /** 242 * Get the additional metadata for a microthesaurus 243 * @param microthesaurusCTypeId The id of content type for this microthesaurus 244 * @return The list of additional metadata definition 245 */ 246 private List<ModelItem> _getModelItems(String microthesaurusCTypeId) 247 { 248 List<ModelItem> modelItems = new ArrayList<>(); 249 ModelItemType contentAttributeType = _contentAttributeTypeExtensionPoint.getExtension(ModelItemTypeConstants.CONTENT_ELEMENT_TYPE_ID); 250 251 // Generic term 252 ContentAttributeDefinition genericTerm = new ContentAttributeDefinition(_contentTypeEP, _contentTypesHelper); 253 genericTerm.setContentTypeId(microthesaurusCTypeId); 254 genericTerm.setName(METADATA_GENERIC_TERM); 255 genericTerm.setMultiple(false); 256 genericTerm.setType(contentAttributeType); 257 genericTerm.setInvertRelationPath(METADATA_SPECIFIC_TERMS); 258 genericTerm.setLabel(new I18nizableText("plugin." + _pluginName, "PLUGINS_THESAURUS_CONTENT_TYPE_TERM_GENERIC_TERMS")); 259 genericTerm.setDescription(new I18nizableText("plugin." + _pluginName, "PLUGINS_THESAURUS_CONTENT_TYPE_TERM_GENERIC_TERMS_DESC")); 260 modelItems.add(genericTerm); 261 262 // Specific terms 263 ContentAttributeDefinition specificTerms = new ContentAttributeDefinition(_contentTypeEP, _contentTypesHelper); 264 specificTerms.setContentTypeId(microthesaurusCTypeId); 265 specificTerms.setName(METADATA_SPECIFIC_TERMS); 266 specificTerms.setMultiple(true); 267 specificTerms.setType(contentAttributeType); 268 specificTerms.setInvertRelationPath(METADATA_GENERIC_TERM); 269 specificTerms.setLabel(new I18nizableText("plugin." + _pluginName, "PLUGINS_THESAURUS_CONTENT_TYPE_TERM_SPECIFIC_TERMS")); 270 specificTerms.setDescription(new I18nizableText("plugin." + _pluginName, "PLUGINS_THESAURUS_CONTENT_TYPE_TERM_SPECIFIC_TERMS_DESC")); 271 modelItems.add(specificTerms); 272 273 // Related terms 274 ContentAttributeDefinition relatedTerms = new ContentAttributeDefinition(_contentTypeEP, _contentTypesHelper); 275 relatedTerms.setContentTypeId(microthesaurusCTypeId); 276 relatedTerms.setName(METADATA_RELATED_TERMS); 277 relatedTerms.setMultiple(true); 278 relatedTerms.setType(contentAttributeType); 279 relatedTerms.setInvertRelationPath(METADATA_RELATED_TERMS); 280 relatedTerms.setLabel(new I18nizableText("plugin." + _pluginName, "PLUGINS_THESAURUS_CONTENT_TYPE_TERM_RELATED_TERMS")); 281 relatedTerms.setDescription(new I18nizableText("plugin." + _pluginName, "PLUGINS_THESAURUS_CONTENT_TYPE_TERM_RELATED_TERMS_DESC")); 282 modelItems.add(relatedTerms); 283 284 return modelItems; 285 } 286 287 /** 288 * Get a thesaurus by its label 289 * @param label the label 290 * @return the thesaurus or null if not found 291 */ 292 public Thesaurus findThesaurusByLabel(String label) 293 { 294 String xpathQuery = "//element(*, " + ThesaurusFactory.THESAURUS_NODETYPE + ")[@ametys-internal:label='" + label + "']"; 295 296 AmetysObjectIterable<Thesaurus> thesaurii = _resolver.query(xpathQuery); 297 return thesaurii.getSize() > 0 ? thesaurii.iterator().next() : null; 298 } 299 300 /** 301 * Creates a thesaurus 302 * @param label The label 303 * @return The id and label of the created microthesaurus 304 */ 305 @Callable 306 public Map<String, Object> createThesaurus (String label) 307 { 308 if (_rightManager.hasRight(_currentUserProvider.getUser(), "Thesaurus_Rights_CreateThesaurus", "/cms") != RightResult.RIGHT_ALLOW) 309 { 310 String errorMessage = "User " + _currentUserProvider.getUser() + " try to create thesaurus with no sufficient rights"; 311 getLogger().error(errorMessage); 312 throw new IllegalStateException(errorMessage); 313 } 314 315 Map<String, Object> result = new HashMap<>(); 316 317 if (findThesaurusByLabel(label) != null) 318 { 319 result.put("success", false); 320 result.put("alreadyExist", true); 321 result.put("label", label); 322 return result; 323 } 324 325 ModifiableTraversableAmetysObject rootNode = getRootNode(); 326 String originalName = FilterNameHelper.filterName(label); 327 328 // Find unique name 329 int index = 2; 330 String name = originalName; 331 while (rootNode.hasChild(name)) 332 { 333 name = originalName + "_" + (index++); 334 } 335 336 Thesaurus thesaurus = rootNode.createChild(name, ThesaurusFactory.THESAURUS_NODETYPE); 337 thesaurus.setLabel(label); 338 339 rootNode.saveChanges(); 340 341 result.put("success", true); 342 result.put("id", thesaurus.getId()); 343 result.put("label", thesaurus.getLabel()); 344 345 return result; 346 } 347 348 /** 349 * Updates a thesaurus 350 * @param thesaurusId The id of microthesaurus 351 * @param label The new label 352 * @return The id and label of the updated microthesaurus 353 * @throws EditContentTypeException if failed to update the existing content types for child microthesaurii. 354 */ 355 @Callable 356 public Map<String, Object> updateThesaurus (String thesaurusId, String label) throws EditContentTypeException 357 { 358 if (_rightManager.hasRight(_currentUserProvider.getUser(), "Thesaurus_Rights_EditThesaurus", "/cms") != RightResult.RIGHT_ALLOW) 359 { 360 String errorMessage = "User " + _currentUserProvider.getUser() + " try to edit thesaurus of id " + thesaurusId + " with no sufficient rights"; 361 getLogger().error(errorMessage); 362 throw new IllegalStateException(errorMessage); 363 } 364 365 Map<String, Object> result = new HashMap<>(); 366 Thesaurus thesaurus = _resolver.resolveById(thesaurusId); 367 368 Thesaurus homonym = findThesaurusByLabel(label); 369 if (homonym != null && !homonym.equals(thesaurus)) 370 { 371 result.put("success", false); 372 result.put("alreadyExist", true); 373 result.put("label", label); 374 return result; 375 } 376 377 // Update category of existing microthesaurii content type 378 Collection<String> microthesaurii = thesaurus.getMicrothesaurii(); 379 if (microthesaurii.size() > 0) 380 { 381 result.put("hasMicrothesaurii", true); 382 for (String microthesaurus: microthesaurii) 383 { 384 ContentType contentType = _contentTypeEP.getExtension(microthesaurus); 385 386 ContentTypeDefinition contentTypeDef = _getMicrothesaurusContentTypeDefinition(contentType.getId(), contentType.getLabel().getLabel(), thesaurus); 387 contentTypeDef.setCategory(new I18nizableText(label)); 388 _editContentTypeHelper.editContentType(contentTypeDef); 389 } 390 } 391 392 thesaurus.setLabel(label); 393 thesaurus.saveChanges(); 394 395 result.put("success", true); 396 result.put("id", thesaurus.getId()); 397 result.put("label", thesaurus.getLabel()); 398 399 return result; 400 } 401 402 /** 403 * Deletes a thesaurus 404 * @param thesaurusId The id of thesaurus 405 * @return The id of the deleted thesaurus 406 */ 407 @Callable 408 public Map<String, Object> deleteThesaurus (String thesaurusId) 409 { 410 if (_rightManager.hasRight(_currentUserProvider.getUser(), "Thesaurus_Rights_EditThesaurus", "/cms") != RightResult.RIGHT_ALLOW) 411 { 412 String errorMessage = "User " + _currentUserProvider.getUser() + " try to delete thesaurus of id " + thesaurusId + " with no sufficient rights"; 413 getLogger().error(errorMessage); 414 throw new IllegalStateException(errorMessage); 415 } 416 417 Map<String, Object> result = new HashMap<>(); 418 419 Thesaurus thesaurus = _resolver.resolveById(thesaurusId); 420 List<String> microthesaurii = thesaurus.getMicrothesaurii(); 421 422 for (String microthesaurusId : microthesaurii) 423 { 424 Map<String, Object> checkResult = checkBeforeMicrothesaurusDeletion(microthesaurusId); 425 boolean checkMicrothesaurusOk = (boolean) checkResult.get("success"); 426 if (!checkMicrothesaurusOk) 427 { 428 result.putAll(checkResult); 429 430 ContentType cType = _contentTypeEP.getExtension(microthesaurusId); 431 432 Map<String, Object> microthesaurusParams = new HashMap<>(); 433 microthesaurusParams.put("id", microthesaurusId); 434 microthesaurusParams.put("label", cType.getLabel()); 435 436 result.put("undeletable-microthesaurus", microthesaurusParams); 437 return result; 438 } 439 } 440 441 442 result.put("id", thesaurus.getId()); 443 result.put("deleted-microthesaurii", new ArrayList<Map<String, Object>>()); 444 445 // Delete microthesaurus first 446 for (String microthesaurusId : microthesaurii) 447 { 448 ContentType cType = _contentTypeEP.getExtension(microthesaurusId); 449 450 Map<String, Object> microthesaurusParams = new HashMap<>(); 451 microthesaurusParams.put("id", microthesaurusId); 452 microthesaurusParams.put("label", cType.getLabel()); 453 454 Map<String, Object> deletionResult = deleteMicrothesaurus(microthesaurusId); 455 boolean deletionOk = (boolean) deletionResult.get("success"); 456 457 if (!deletionOk) 458 { 459 result.put("success", false); 460 result.put("undeleted-microthesaurus", microthesaurusParams); 461 return result; 462 } 463 else 464 { 465 @SuppressWarnings("unchecked") 466 List<Map<String, Object>> deletedMT = (List<Map<String, Object>>) result.get("deleted-microthesaurii"); 467 deletedMT.add(microthesaurusParams); 468 } 469 } 470 471 // Delete thesaurus 472 thesaurus.remove(); 473 thesaurus.saveChanges(); 474 475 result.put("success", true); 476 return result; 477 } 478 479 /** 480 * Get the label of thesaurus 481 * @param thesaurusId The id of thesaurus 482 * @return The id and label of the thesaurus 483 */ 484 @Callable 485 public String getThesaurusLabel (String thesaurusId) 486 { 487 Thesaurus thesaurus = _resolver.resolveById(thesaurusId); 488 return thesaurus.getLabel(); 489 } 490 491 /** 492 * Get the all terms of a microthesaurus 493 * @param microthesaurusId the id of microthesaurus 494 * @return all terms 495 */ 496 public AmetysObjectIterable<Content> getAllTerms(String microthesaurusId) 497 { 498 Expression cTypeExpr = new ContentTypeExpression(Operator.EQ, microthesaurusId); 499 String xPathQuery = ContentQueryHelper.getContentXPathQuery(cTypeExpr); 500 501 AmetysObjectIterable<Content> terms = _resolver.query(xPathQuery); 502 return terms; 503 } 504 505 /** 506 * Get the root terms of a microthesaurus 507 * @param microthesaurusId the id of microthesaurus 508 * @return root terms 509 */ 510 public AmetysObjectIterable<Content> getRootTerms(String microthesaurusId) 511 { 512 Expression cTypeExpr = new ContentTypeExpression(Operator.EQ, microthesaurusId); 513 Expression genericExpr = new NotExpression(new MetadataExpression(METADATA_GENERIC_TERM)); 514 String xPathQuery = ContentQueryHelper.getContentXPathQuery(new AndExpression(cTypeExpr, genericExpr)); 515 516 AmetysObjectIterable<Content> terms = _resolver.query(xPathQuery); 517 return terms; 518 } 519 520 /** 521 * Get direct child terms of a term 522 * @param microthesaurusId the id of microthesaurus 523 * @param parentId The parent Id 524 * @return The child terms 525 */ 526 public AmetysObjectIterable<Content> getChildTerms(String microthesaurusId, String parentId) 527 { 528 Expression cTypeExpr = new ContentTypeExpression(Operator.EQ, microthesaurusId); 529 Expression genericExpr = new StringExpression(METADATA_GENERIC_TERM, Operator.EQ, parentId); 530 String xPathQuery = ContentQueryHelper.getContentXPathQuery(new AndExpression(cTypeExpr, genericExpr)); 531 532 AmetysObjectIterable<Content> terms = _resolver.query(xPathQuery); 533 return terms; 534 } 535 536 /** 537 * Determines if a term which belongs to a microthesaurus is referenced by contents outside the microthesaurus 538 * @param microthesaurusId the id of microthesaurus 539 * @param term the content term 540 * @return true if the term has at least one referencing content outside the microthesaurus 541 */ 542 public boolean hasReferencingContents(String microthesaurusId, Content term) 543 { 544 Collection<Content> referencingContents = term.getReferencingContents(); 545 546 for (Content referencingContent : referencingContents) 547 { 548 if (!ArrayUtils.contains(referencingContent.getTypes(), microthesaurusId)) 549 { 550 return true; 551 } 552 } 553 554 return false; 555 } 556 557 /** 558 * Creates a microthesaurus 559 * @param label The label 560 * @param thesaurusId The id of parent thesaurus 561 * @param microthesaurusName the name of the microthesaurus 562 * @return The id and label of the created microthesaurus 563 * @throws EditContentTypeException if failed to create the microthesaurus content type 564 */ 565 @Callable 566 public Map<String, Object> createMicrothesaurus (String label, String thesaurusId, String microthesaurusName) throws EditContentTypeException 567 { 568 Thesaurus thesaurus = _resolver.resolveById(thesaurusId); 569 570 Map<String, Object> result = new HashMap<>(); 571 572 String cTypeId = MICROTHESAURUS_CONTENT_TYPE_PREFIX + microthesaurusName; 573 574 List<String> microthesaurii = getMicrothesaurii(); 575 576 // Check if microthesaurus does not already exist 577 if (microthesaurii.contains(cTypeId)) 578 { 579 result.put("success", false); 580 result.put("alreadyExist", microthesaurusName); 581 582 int index = 2; 583 while (microthesaurii.contains(cTypeId)) 584 { 585 cTypeId = MICROTHESAURUS_CONTENT_TYPE_PREFIX + microthesaurusName + "-" + index; 586 index++; 587 } 588 589 result.put("suggestedName", StringUtils.substringAfter(cTypeId, MICROTHESAURUS_CONTENT_TYPE_PREFIX)); 590 return result; 591 } 592 593 // Create content type 594 ContentTypeDefinition contentType = _getMicrothesaurusContentTypeDefinition(cTypeId, label, thesaurus); 595 _editContentTypeHelper.createContentType(contentType); 596 597 thesaurus.addMicrothesaurus(cTypeId); 598 thesaurus.saveChanges(); 599 600 result.put("success", true); 601 result.put("id", microthesaurusName); 602 result.put("label", label); 603 result.put("thesaurusId", thesaurusId); 604 605 return result; 606 } 607 608 /** 609 * Edit a microthesaurus 610 * @param microthesaurusId The id of microthesaurus to edit 611 * @param label The new label 612 * @return The id and label of the updated microthesaurus 613 * @throws EditContentTypeException if failed to update the content type for this microthesaurus 614 */ 615 @Callable 616 public Map<String, Object> updateMicrothesaurus (String microthesaurusId, String label) throws EditContentTypeException 617 { 618 if (_rightManager.hasRight(_currentUserProvider.getUser(), "Thesaurus_Rights_EditMicrothesaurus", "/cms") != RightResult.RIGHT_ALLOW) 619 { 620 String errorMessage = "User " + _currentUserProvider.getUser() + " try to edit microthesaurus of id '" + microthesaurusId + "' with no sufficient rights"; 621 getLogger().error(errorMessage); 622 throw new IllegalStateException(errorMessage); 623 } 624 625 ContentType microThesaurus = _contentTypeEP.getExtension(microthesaurusId); 626 627 Thesaurus thesaurus = getParentThesaurus(microThesaurus); 628 629 // Update content type 630 ContentTypeDefinition contentTypeDef = _getMicrothesaurusContentTypeDefinition(microThesaurus.getId(), label, thesaurus); 631 _editContentTypeHelper.editContentType(contentTypeDef); 632 633 Map<String, Object> result = new HashMap<>(); 634 result.put("id", microThesaurus.getId()); 635 result.put("label", microThesaurus.getLabel()); 636 result.put("thesaurusId", thesaurus.getId()); 637 result.put("success", true); 638 639 return result; 640 } 641 642 /** 643 * Do some check before microthesaurus deletion 644 * @param microthesaurusId the id of microthesaurus 645 * @return the check result 646 */ 647 @Callable 648 public Map<String, Object> checkBeforeMicrothesaurusDeletion(String microthesaurusId) 649 { 650 Map<String, Object> result = new HashMap<>(); 651 652 AmetysObjectIterable<Content> terms = getAllTerms(microthesaurusId); 653 if (terms.getSize() == 0) 654 { 655 result.put("success", true); 656 result.put("emptyMicrothesaurus", true); 657 return result; 658 } 659 660 for (Content term : terms) 661 { 662 if (hasReferencingContents(microthesaurusId, term)) 663 { 664 result.put("success", false); 665 result.put("hasReferencingContents", true); 666 return result; 667 } 668 } 669 670 result.put("success", true); 671 return result; 672 } 673 674 /** 675 * Deletes a microthesaurus 676 * @param microthesaurusId The id of microthesaurus 677 * @return The id of the deleted microthesaurus 678 */ 679 @Callable 680 public Map<String, Object> deleteMicrothesaurus(String microthesaurusId) 681 { 682 if (_rightManager.hasRight(_currentUserProvider.getUser(), "Thesaurus_Rights_EditMicrothesaurus", "/cms") != RightResult.RIGHT_ALLOW) 683 { 684 String errorMessage = "User " + _currentUserProvider.getUser() + " try to delete microthesaurus of id '" + microthesaurusId + "' with no sufficient rights"; 685 getLogger().error(errorMessage); 686 throw new IllegalStateException(errorMessage); 687 } 688 689 Map<String, Object> result = checkBeforeMicrothesaurusDeletion(microthesaurusId); 690 691 boolean checkOk = (boolean) result.get("success"); 692 if (!checkOk) 693 { 694 return result; 695 } 696 697 // Remove existing terms 698 AmetysObjectIterable<Content> terms = getAllTerms(microthesaurusId); 699 for (Content term : terms) 700 { 701 _deleteTerm(term); 702 } 703 704 ContentType cType = _contentTypeEP.getExtension(microthesaurusId); 705 Thesaurus thesaurus = getParentThesaurus(cType); 706 707 result.put("thesaurusId", thesaurus.getId()); 708 result.put("id", microthesaurusId); 709 result.put("label", cType.getLabel()); 710 711 try 712 { 713 _editContentTypeHelper.removeContentType(microthesaurusId); 714 } 715 catch (RemoveContentTypeException e) 716 { 717 getLogger().error("The microthesaurus with id {} can not be removed", microthesaurusId, e); 718 result.put("success", false); 719 720 return result; 721 } 722 723 // Remove thesaurus from parent thesaurus 724 thesaurus.removeMicrothesaurus(microthesaurusId); 725 thesaurus.saveChanges(); 726 727 result.put("success", true); 728 729 return result; 730 } 731 732 private void _deleteTerm (Content term) 733 { 734 Map<String, Object> eventParams = new HashMap<>(); 735 eventParams.put(ObservationConstants.ARGS_CONTENT, term); 736 eventParams.put(ObservationConstants.ARGS_CONTENT_NAME, term.getName()); 737 eventParams.put(ObservationConstants.ARGS_CONTENT_ID, term.getId()); 738 739 _observationManager.notify(new Event(ObservationConstants.EVENT_CONTENT_DELETING, _currentUserProvider.getUser(), eventParams)); 740 741 RemovableAmetysObject removableContent = (RemovableAmetysObject) term; 742 743 // Remove the content. 744 removableContent.remove(); 745 746 _observationManager.notify(new Event(ObservationConstants.EVENT_CONTENT_DELETED, _currentUserProvider.getUser(), eventParams)); 747 } 748 /** 749 * Get the root plugin storage object. 750 * @return the root plugin storage object. 751 * @throws AmetysRepositoryException if a repository error occurs. 752 */ 753 public ModifiableTraversableAmetysObject getRootNode() throws AmetysRepositoryException 754 { 755 try 756 { 757 ModifiableTraversableAmetysObject pluginsNode = _resolver.resolveByPath("/ametys:plugins"); 758 759 ModifiableTraversableAmetysObject pluginNode = _getOrCreateNode(pluginsNode, __PLUGIN_NODE_NAME, "ametys:unstructured"); 760 761 return _getOrCreateNode(pluginNode, "ametys:thesaurii", __ROOT_THESAURII_NODETYPE); 762 } 763 catch (AmetysRepositoryException e) 764 { 765 throw new AmetysRepositoryException("Unable to get the thesaurus root node", e); 766 } 767 } 768 769 /** 770 * Get or create a node 771 * @param parentNode the parent node 772 * @param nodeName the name of the node 773 * @param nodeType the type of the node 774 * @return The retrieved or created node 775 * @throws AmetysRepositoryException if an error occurs when manipulating the repository 776 */ 777 protected ModifiableTraversableAmetysObject _getOrCreateNode(ModifiableTraversableAmetysObject parentNode, String nodeName, String nodeType) throws AmetysRepositoryException 778 { 779 ModifiableTraversableAmetysObject definitionsNode; 780 if (parentNode.hasChild(nodeName)) 781 { 782 definitionsNode = parentNode.getChild(nodeName); 783 } 784 else 785 { 786 definitionsNode = parentNode.createChild(nodeName, nodeType); 787 parentNode.saveChanges(); 788 } 789 return definitionsNode; 790 } 791}