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