001/* 002 * Copyright 2014 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.contenttype; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.Comparator; 023import java.util.HashMap; 024import java.util.HashSet; 025import java.util.Iterator; 026import java.util.LinkedHashMap; 027import java.util.LinkedHashSet; 028import java.util.List; 029import java.util.Map; 030import java.util.Optional; 031import java.util.Set; 032import java.util.TreeSet; 033import java.util.function.Predicate; 034 035import org.apache.avalon.framework.activity.Disposable; 036import org.apache.avalon.framework.component.Component; 037import org.apache.avalon.framework.configuration.ConfigurationException; 038import org.apache.avalon.framework.logger.AbstractLogEnabled; 039import org.apache.avalon.framework.service.ServiceException; 040import org.apache.avalon.framework.service.ServiceManager; 041import org.apache.avalon.framework.service.Serviceable; 042import org.apache.avalon.framework.thread.ThreadSafe; 043import org.apache.commons.collections.CollectionUtils; 044import org.apache.commons.lang3.ArrayUtils; 045import org.apache.commons.lang3.StringUtils; 046import org.apache.commons.lang3.tuple.Pair; 047 048import org.ametys.cms.content.RootContentHelper; 049import org.ametys.cms.contenttype.indexing.IndexingField; 050import org.ametys.cms.contenttype.indexing.IndexingModel; 051import org.ametys.cms.repository.Content; 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.core.user.UserIdentity; 057import org.ametys.plugins.repository.AmetysRepositoryException; 058import org.ametys.runtime.i18n.I18nizableText; 059import org.ametys.runtime.model.ModelItem; 060import org.ametys.runtime.model.View; 061import org.ametys.runtime.model.ViewItem; 062 063/** 064 * Helper for manipulating {@link ContentType}s 065 * 066 */ 067public class ContentTypesHelper extends AbstractLogEnabled implements Component, Serviceable, ThreadSafe, Disposable 068{ 069 /** The Avalon role */ 070 public static final String ROLE = ContentTypesHelper.class.getName(); 071 072 /** The content types extension point */ 073 protected ContentTypeExtensionPoint _cTypeEP; 074 /** The current user provider */ 075 protected CurrentUserProvider _userProvider; 076 /** The rights manager */ 077 protected RightManager _rightManager; 078 /** Helper for root content */ 079 protected RootContentHelper _rootContentHelper; 080 081 private DynamicContentTypeDescriptorExtentionPoint _dynamicCTDescriptorEP; 082 083 /** 084 * Cache containing view metadata sets 085 * @deprecated use {@link #_viewCache} instead 086 */ 087 @Deprecated 088 private Map<String, Map<String, MetadataSet>> _cacheForView = new HashMap<>(); 089 090 /** 091 * Cache containing edition metadata sets 092 * @deprecated use {@link #_viewCache} instead 093 */ 094 @Deprecated 095 private Map<String, Map<String, MetadataSet>> _cacheForEdition = new HashMap<>(); 096 097 /** 098 * Cache containing content types' views 099 * The key is a cache identifier that is a concatenation of the identifiers of the concerned content types and mixins 100 * The value is a Map containing the views by their names 101 */ 102 private Map<String, Map<String, View>> _viewCache = new HashMap<>(); 103 104 private ServiceManager _smanager; 105 106 @Override 107 public void service(ServiceManager smanager) throws ServiceException 108 { 109 _smanager = smanager; 110 _userProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 111 _rightManager = (RightManager) smanager.lookup(RightManager.ROLE); 112 _rootContentHelper = (RootContentHelper) smanager.lookup(RootContentHelper.ROLE); 113 } 114 115 private DynamicContentTypeDescriptorExtentionPoint _getDynamicContentTypeDescriptorExtentionPoint() 116 { 117 if (_dynamicCTDescriptorEP == null) 118 { 119 try 120 { 121 _dynamicCTDescriptorEP = (DynamicContentTypeDescriptorExtentionPoint) _smanager.lookup(DynamicContentTypeDescriptorExtentionPoint.ROLE); 122 } 123 catch (ServiceException e) 124 { 125 throw new IllegalStateException(e); 126 } 127 } 128 return _dynamicCTDescriptorEP; 129 } 130 131 @Override 132 public void dispose() 133 { 134 _cacheForView = new HashMap<>(); 135 _cacheForEdition = new HashMap<>(); 136 _viewCache = new HashMap<>(); 137 } 138 139 /** 140 * Lazy lookup of {@link ContentTypeExtensionPoint} 141 * @return the content type extension point 142 */ 143 protected ContentTypeExtensionPoint _getContentTypeEP() 144 { 145 if (_cTypeEP == null) 146 { 147 try 148 { 149 _cTypeEP = (ContentTypeExtensionPoint) _smanager.lookup(ContentTypeExtensionPoint.ROLE); 150 } 151 catch (ServiceException e) 152 { 153 throw new RuntimeException("Unable to lookup ContentTypeExtensionPoint component", e); 154 } 155 } 156 return _cTypeEP; 157 } 158 159 /** 160 * Determines if a content is a instance of given content type id 161 * 162 * @param content The content 163 * @param cTypeId The id of content type or mixin 164 * @return <code>true</code> if the content is an instance of content type 165 */ 166 public boolean isInstanceOf(Content content, String cTypeId) 167 { 168 String[] types = content.getTypes(); 169 if (ArrayUtils.contains(types, cTypeId)) 170 { 171 return true; 172 } 173 174 String[] mixins = content.getMixinTypes(); 175 if (ArrayUtils.contains(mixins, cTypeId)) 176 { 177 return true; 178 } 179 180 return _containsContentType(ArrayUtils.addAll(types, mixins), cTypeId); 181 } 182 183 private boolean _containsContentType(String[] cTypesId, String cTypeId) 184 { 185 for (String id : cTypesId) 186 { 187 ContentType cType = _getContentTypeEP().getExtension(id); 188 if (cType != null) 189 { 190 if (ArrayUtils.contains(cType.getSupertypeIds(), cTypeId)) 191 { 192 return true; 193 } 194 else if (_containsContentType(cType.getSupertypeIds(), cTypeId)) 195 { 196 return true; 197 } 198 } 199 } 200 return false; 201 } 202 203 /** 204 * Get the id of the content type common ancestor 205 * 206 * @param contentTypes The content types to compare 207 * @return The id of common ancestor or <code>null</code> if not found 208 */ 209 public String getCommonAncestor(Collection<String> contentTypes) 210 { 211 if (contentTypes.isEmpty()) 212 { 213 return null; 214 } 215 216 if (contentTypes.size() == 1) 217 { 218 return contentTypes.iterator().next(); 219 } 220 221 List<Collection<String>> superTypesByCType = new ArrayList<>(); 222 for (String id : contentTypes) 223 { 224 Set<String> superTypes = new HashSet<>(); 225 226 superTypes.add(id); 227 superTypes.addAll(getAncestors(id)); 228 229 superTypesByCType.add(superTypes); 230 } 231 232 Iterator<Collection<String>> superTypesByCTypeIt = superTypesByCType.iterator(); 233 234 Set<String> commonCTypes = new HashSet<>(superTypesByCTypeIt.next()); 235 while (superTypesByCTypeIt.hasNext() && !commonCTypes.isEmpty()) 236 { 237 commonCTypes.retainAll(superTypesByCTypeIt.next()); 238 } 239 240 commonCTypes = removeAncestors(commonCTypes); 241 242 if (commonCTypes.size() == 1) 243 { 244 return commonCTypes.iterator().next(); 245 } 246 247 return null; // No common ancestor 248 } 249 250 /** 251 * Remove all content types in the set that are ancestors of other content types in the set. 252 * @param contentTypes a Set of content type IDs. 253 * @return a Set of content type IDs without ancestors. 254 */ 255 protected Set<String> removeAncestors(Set<String> contentTypes) 256 { 257 Set<String> noAncestors = new HashSet<>(contentTypes); 258 259 Iterator<String> it1 = contentTypes.iterator(); 260 while (it1.hasNext()) 261 { 262 ContentType cType1 = _getContentTypeEP().getExtension(it1.next()); 263 264 Iterator<String> it2 = contentTypes.iterator(); 265 while (it2.hasNext()) 266 { 267 String cType2 = it2.next(); 268 String[] supertypeIds = cType1.getSupertypeIds(); 269 270 // CType2 is an ancestor of CType1: remove it. 271 if (ArrayUtils.contains(supertypeIds, cType2)) 272 { 273 noAncestors.remove(cType2); 274 } 275 } 276 } 277 278 return noAncestors; 279 } 280 281 /** 282 * Get all ancestors for the given content type 283 * 284 * @param contentTypeId The content type id to test 285 * @return A non-null set of all ancestors. Does not contains the contentTypeId itself. 286 * @throws IllegalArgumentException if the content type does not exist. 287 */ 288 public Set<String> getAncestors(String contentTypeId) 289 { 290 Set<String> superTypes = new HashSet<>(); 291 292 ContentType cType = _getContentTypeEP().getExtension(contentTypeId); 293 294 if (cType == null) 295 { 296 throw new IllegalArgumentException("Unable to get anscestors of unknown content type '" + contentTypeId + "'"); 297 } 298 299 300 String[] supertypeIds = cType.getSupertypeIds(); 301 302 for (String superTypeId : supertypeIds) 303 { 304 superTypes.add(superTypeId); 305 superTypes.addAll(getAncestors(superTypeId)); 306 } 307 308 return superTypes; 309 310 } 311 312 /** 313 * Get super type's ids for the given content type 314 * The first entry contains super content types, the second one contains super mixin types 315 * 316 * @param contentTypeId The content type id to test 317 * @return An array of super type's ids. 318 * @throws IllegalArgumentException if the content type does not exist. 319 */ 320 public Pair<String[], String[]> getSupertypeIds(String contentTypeId) 321 { 322 ContentType contentType = _getContentTypeEP().getExtension(contentTypeId); 323 if (contentType == null) 324 { 325 throw new IllegalArgumentException("Unable to get super types of unknown content type '" + contentTypeId + "'"); 326 } 327 328 List<String> superMixins = new ArrayList<>(); 329 List<String> superContentTypes = new ArrayList<>(); 330 for (String supertypeId : contentType.getSupertypeIds()) 331 { 332 ContentType supertype = _getContentTypeEP().getExtension(supertypeId); 333 if (supertype == null) 334 { 335 throw new IllegalArgumentException("Unable to get the unknown super type '" + supertypeId + "' for type '" + contentTypeId + "'"); 336 } 337 338 if (supertype.isMixin()) 339 { 340 superMixins.add(supertypeId); 341 } 342 else 343 { 344 superContentTypes.add(supertypeId); 345 } 346 } 347 348 String[] superContentTypesArray = superContentTypes.toArray(new String[superContentTypes.size()]); 349 String[] superMixinsArray = superMixins.toArray(new String[superMixins.size()]); 350 return Pair.of(superContentTypesArray, superMixinsArray); 351 } 352 353 /** 354 * Get plugin name for the given content type 355 * 356 * @param contentTypeId The content type id to test 357 * @return the plugin name 358 * @throws IllegalArgumentException if the content type does not exist. 359 */ 360 public String getPluginName(String contentTypeId) 361 { 362 ContentType cType = _getContentTypeEP().getExtension(contentTypeId); 363 if (cType == null) 364 { 365 throw new IllegalArgumentException("Unable to get plugin name of unknown content type '" + contentTypeId + "'"); 366 } 367 368 return cType.getPluginName(); 369 } 370 371 /** 372 * Builds the reverse hierarchies of ancestors of a content type 373 * @param contentTypeId The content type's id 374 * @return the reverse hierarchies with ancestors 375 */ 376 public List<Set<String>> buildReverseHierarchies(String contentTypeId) 377 { 378 Set<String> hierarchy = new LinkedHashSet<>(); 379 hierarchy.add(contentTypeId); 380 return _buildReverseHierarchies (contentTypeId, hierarchy); 381 } 382 383 private List<Set<String>> _buildReverseHierarchies(String contentTypeId, Set<String> hierarchy) 384 { 385 List<Set<String>> hierarchies = new ArrayList<>(); 386 387 ContentType cType = _getContentTypeEP().getExtension(contentTypeId); 388 if (cType == null) 389 { 390 throw new IllegalArgumentException("Unable to get anscestors of unknown content type '" + contentTypeId + "'"); 391 } 392 393 String[] supertypeIds = cType.getSupertypeIds(); 394 if (supertypeIds.length > 0) 395 { 396 for (String superTypeId : supertypeIds) 397 { 398 Set<String> superHierarchy = new LinkedHashSet<>(hierarchy); 399 superHierarchy.add(superTypeId); 400 hierarchies.addAll(_buildReverseHierarchies(superTypeId, superHierarchy)); 401 } 402 } 403 else 404 { 405 hierarchies.add(hierarchy); 406 } 407 408 return hierarchies; 409 } 410 411 /** 412 * Retrieves the root metadata names of a content. 413 * @param content The content. 414 * @return the metadata names. 415 */ 416 public Set<String> getMetadataNames(Content content) 417 { 418 return getMetadataNames(content.getTypes(), content.getMixinTypes()); 419 } 420 421 /** 422 * Retrieves the metadata names resulting of the union of metadata 423 * names of given content types and mixins 424 * 425 * @param cTypes The id of content types 426 * @param mixins The id of mixins 427 * @return the metadata names. 428 */ 429 public Set<String> getMetadataNames(String[] cTypes, String[] mixins) 430 { 431 Set<String> metadataNames = new HashSet<>(); 432 433 for (String id : cTypes) 434 { 435 ContentType cType = _getContentTypeEP().getExtension(id); 436 metadataNames.addAll(cType.getMetadataNames()); 437 } 438 439 for (String id : mixins) 440 { 441 ContentType cType = _getContentTypeEP().getExtension(id); 442 metadataNames.addAll(cType.getMetadataNames()); 443 } 444 445 return metadataNames; 446 } 447 448 /** 449 * Get all views resulting of the concatenation of views of given content types and mixins. 450 * @param contentTypeIds the identifiers of the content types 451 * @param mixinIds the identifiers of the mixins 452 * @return The views 453 */ 454 public Map<String, View> getViews(String[] contentTypeIds, String[] mixinIds) 455 { 456 return getViews(contentTypeIds, mixinIds, Collections.emptySet()); 457 } 458 459 /** 460 * Get all views resulting of the concatenation of views of given content types and mixins. 461 * @param contentTypeIds the identifiers of the content types 462 * @param mixinIds the identifiers of the mixins 463 * @param viewNamesToAvoid names of views that should not be managed 464 * @return The views 465 */ 466 public Map<String, View> getViews(String[] contentTypeIds, String[] mixinIds, Set<String> viewNamesToAvoid) 467 { 468 Map<String, View> views = new HashMap<>(); 469 470 for (String contentTypeId : contentTypeIds) 471 { 472 ContentType contentType = _getContentTypeEP().getExtension(contentTypeId); 473 for (String viewName : contentType.getViewNames()) 474 { 475 if (!viewNamesToAvoid.contains(viewName) && !views.containsKey(viewName)) 476 { 477 views.put(viewName, getView(viewName, contentTypeIds, mixinIds)); 478 } 479 } 480 } 481 482 return views; 483 } 484 485 /** 486 * Get the view for view resulting of the concatenation of views of the given content types and mixins. 487 * @param viewName the name of the view to retrieve 488 * @param contentTypeIds the identifiers of the content types 489 * @param mixinIds the identifiers of the mixins 490 * @return The view. 491 */ 492 public View getView(String viewName, String[] contentTypeIds, String[] mixinIds) 493 { 494 String cacheId = _getCacheIdentifier(contentTypeIds, mixinIds); 495 496 if (_viewCache.containsKey(cacheId) && _viewCache.get(cacheId).containsKey(viewName)) 497 { 498 return _viewCache.get(cacheId).get(viewName); 499 } 500 501 View joinView = new View(); 502 boolean foundOne = false; 503 504 for (String contentTypeId : contentTypeIds) 505 { 506 ContentType contentType = _getContentTypeEP().getExtension(contentTypeId); 507 508 View view = contentType.getView(viewName); 509 if (view != null) 510 { 511 foundOne = true; 512 joinView.includeView(view); 513 514 if (joinView.getName() == null) 515 { 516 joinView.setName(viewName); 517 joinView.setLabel(view.getLabel()); 518 joinView.setDescription(view.getDescription()); 519 joinView.setIconGlyph(view.getIconGlyph()); 520 joinView.setIconDecorator(view.getIconDecorator()); 521 joinView.setSmallIcon(view.getSmallIcon()); 522 joinView.setMediumIcon(view.getMediumIcon()); 523 joinView.setLargeIcon(view.getLargeIcon()); 524 } 525 } 526 } 527 528 for (String id : mixinIds) 529 { 530 ContentType mixin = _getContentTypeEP().getExtension(id); 531 532 View view = mixin.getView(viewName); 533 if (view != null) 534 { 535 foundOne = true; 536 joinView.includeView(view); 537 } 538 } 539 540 if (!foundOne) 541 { 542 return null; 543 } 544 545 if (!_viewCache.containsKey(cacheId)) 546 { 547 _viewCache.put(cacheId, new HashMap<String, View>()); 548 } 549 _viewCache.get(cacheId).put(viewName, joinView); 550 551 return joinView; 552 } 553 554 /** 555 * Get all metadata sets for view resulting of the concatenation of metadata 556 * sets of given content types and mixins. 557 * 558 * @param cTypes The id of content types 559 * @param mixins The id of mixins 560 * @param includeInternal <code>true</code> True to include internal 561 * metadata sets 562 * @return The metadata sets 563 * @deprecated Use {@link #getViews(String[], String[])} instead 564 */ 565 @Deprecated 566 public Map<String, MetadataSet> getMetadataSetsForView(String[] cTypes, String[] mixins, boolean includeInternal) 567 { 568 Map<String, MetadataSet> metadataSets = new HashMap<>(); 569 570 Set<String> viewMetadataSetNames = new HashSet<>(); 571 for (String id : cTypes) 572 { 573 ContentType cType = _getContentTypeEP().getExtension(id); 574 for (String name : cType.getViewMetadataSetNames(includeInternal)) 575 { 576 if (!viewMetadataSetNames.contains(name)) 577 { 578 viewMetadataSetNames.add(name); 579 } 580 } 581 } 582 583 for (String viewMetadataSetName : viewMetadataSetNames) 584 { 585 metadataSets.put(viewMetadataSetName, getMetadataSetForView(viewMetadataSetName, cTypes, mixins)); 586 } 587 588 return metadataSets; 589 } 590 591 /** 592 * Get all metadata sets for edition resulting of the concatenation of 593 * metadata sets of given content types and mixins. 594 * 595 * @param cTypes The id of content types 596 * @param mixins The id of mixins 597 * @param includeInternal <code>true</code> True to include internal 598 * metadata sets 599 * @return The metadata sets 600 * @deprecated Use {@link #getViews(String[], String[])} instead 601 */ 602 @Deprecated 603 public Map<String, MetadataSet> getMetadataSetsForEdition(String[] cTypes, String[] mixins, boolean includeInternal) 604 { 605 Map<String, MetadataSet> metadataSets = new HashMap<>(); 606 607 Set<String> editionMetadataSetNames = new HashSet<>(); 608 for (String id : cTypes) 609 { 610 ContentType cType = _getContentTypeEP().getExtension(id); 611 for (String name : cType.getEditionMetadataSetNames(includeInternal)) 612 { 613 if (!editionMetadataSetNames.contains(name)) 614 { 615 editionMetadataSetNames.add(name); 616 } 617 } 618 } 619 620 for (String editionMetadataSetName : editionMetadataSetNames) 621 { 622 metadataSets.put(editionMetadataSetName, getMetadataSetForEdition(editionMetadataSetName, cTypes, mixins)); 623 } 624 625 return metadataSets; 626 } 627 628 /** 629 * Get the metadata set for view resulting of the concatenation of metadata 630 * sets of given content types and mixins. 631 * 632 * @param metadataSetName the name of metadata set to retrieve 633 * @param cTypes The id of content types 634 * @param mixins The id of mixins 635 * @return The list of metadata set names. 636 * @deprecated Use {@link #getView(String, String[], String[])} instead 637 */ 638 @Deprecated 639 public MetadataSet getMetadataSetForView(String metadataSetName, String[] cTypes, String[] mixins) 640 { 641 String cacheId = _getCacheIdentifier(cTypes, mixins); 642 643 if (_cacheForView.containsKey(cacheId) && _cacheForView.get(cacheId).containsKey(metadataSetName)) 644 { 645 return _cacheForView.get(cacheId).get(metadataSetName); 646 } 647 648 MetadataSet joinMetadataSet = new MetadataSet(); 649 boolean foundOne = false; 650 651 for (String id : cTypes) 652 { 653 ContentType cType = _getContentTypeEP().getExtension(id); 654 655 MetadataSet metadataSet = cType.getMetadataSetForView(metadataSetName); 656 if (metadataSet != null) 657 { 658 foundOne = true; 659 copyMetadataSetElementsIfNotExist(metadataSet, joinMetadataSet); 660 661 if (joinMetadataSet.getName() == null) 662 { 663 joinMetadataSet.setName(metadataSetName); 664 joinMetadataSet.setLabel(metadataSet.getLabel()); 665 joinMetadataSet.setDescription(metadataSet.getDescription()); 666 joinMetadataSet.setIconGlyph(metadataSet.getIconGlyph()); 667 joinMetadataSet.setIconDecorator(metadataSet.getIconDecorator()); 668 joinMetadataSet.setSmallIcon(metadataSet.getSmallIcon()); 669 joinMetadataSet.setMediumIcon(metadataSet.getMediumIcon()); 670 joinMetadataSet.setLargeIcon(metadataSet.getLargeIcon()); 671 joinMetadataSet.setEdition(false); 672 } 673 } 674 } 675 676 for (String id : mixins) 677 { 678 ContentType mixin = _getContentTypeEP().getExtension(id); 679 680 MetadataSet metadataSet = mixin.getMetadataSetForView(metadataSetName); 681 if (metadataSet != null) 682 { 683 foundOne = true; 684 copyMetadataSetElementsIfNotExist(metadataSet, joinMetadataSet); 685 } 686 } 687 688 if (!foundOne) 689 { 690 return null; 691 } 692 693 if (!_cacheForView.containsKey(cacheId)) 694 { 695 _cacheForView.put(cacheId, new HashMap<String, MetadataSet>()); 696 } 697 _cacheForView.get(cacheId).put(metadataSetName, joinMetadataSet); 698 699 return joinMetadataSet; 700 } 701 702 /** 703 * Get the metadata set for edition resulting of the concatenation of 704 * metadata sets of given content types and mixins. 705 * 706 * @param metadataSetName the name of metadata set to retrieve 707 * @param cTypes The id of content types 708 * @param mixins The id of mixins 709 * @return The metadata set 710 * @deprecated Use {@link #getView(String, String[], String[])} instead 711 */ 712 @Deprecated 713 public MetadataSet getMetadataSetForEdition(String metadataSetName, String[] cTypes, String[] mixins) 714 { 715 String cacheId = _getCacheIdentifier(cTypes, mixins); 716 717 if (_cacheForEdition.containsKey(cacheId) && _cacheForEdition.get(cacheId).containsKey(metadataSetName)) 718 { 719 return _cacheForEdition.get(cacheId).get(metadataSetName); 720 } 721 722 MetadataSet joinMetadataSet = new MetadataSet(); 723 boolean foundOne = false; 724 725 for (String id : cTypes) 726 { 727 ContentType cType = _getContentTypeEP().getExtension(id); 728 729 MetadataSet metadataSet = cType.getMetadataSetForEdition(metadataSetName); 730 if (metadataSet != null) 731 { 732 foundOne = true; 733 copyMetadataSetElementsIfNotExist(metadataSet, joinMetadataSet); 734 735 if (joinMetadataSet.getName() == null) 736 { 737 joinMetadataSet.setName(metadataSetName); 738 joinMetadataSet.setLabel(metadataSet.getLabel()); 739 joinMetadataSet.setDescription(metadataSet.getDescription()); 740 joinMetadataSet.setIconGlyph(metadataSet.getIconGlyph()); 741 joinMetadataSet.setIconDecorator(metadataSet.getIconDecorator()); 742 joinMetadataSet.setSmallIcon(metadataSet.getSmallIcon()); 743 joinMetadataSet.setMediumIcon(metadataSet.getMediumIcon()); 744 joinMetadataSet.setLargeIcon(metadataSet.getLargeIcon()); 745 joinMetadataSet.setEdition(true); 746 } 747 } 748 } 749 750 for (String id : mixins) 751 { 752 ContentType mixin = _getContentTypeEP().getExtension(id); 753 754 MetadataSet metadataSet = mixin.getMetadataSetForEdition(metadataSetName); 755 if (metadataSet != null) 756 { 757 foundOne = true; 758 copyMetadataSetElementsIfNotExist(metadataSet, joinMetadataSet); 759 } 760 } 761 762 if (!foundOne) 763 { 764 return null; 765 } 766 767 if (!_cacheForEdition.containsKey(cacheId)) 768 { 769 _cacheForEdition.put(cacheId, new HashMap<String, MetadataSet>()); 770 } 771 _cacheForEdition.get(cacheId).put(metadataSetName, joinMetadataSet); 772 773 return joinMetadataSet; 774 } 775 776 /** 777 * Get the metadata sets of a content type 778 * @param cTypeId the content type id 779 * @param edition Set to true to get edition metadata set. False otherwise. 780 * @param includeInternal Set to true to include internal metadata sets. 781 * @return the metadata sets info 782 * @deprecated Use {@link View} API instead 783 */ 784 @Deprecated 785 @Callable 786 public List<Map<String, Object>> getMetadataSetsInfo(String cTypeId, boolean edition, boolean includeInternal) 787 { 788 List<Map<String, Object>> metadataSets = new ArrayList<>(); 789 790 ContentType cType = _getContentTypeEP().getExtension(cTypeId); 791 792 Set<String> metadataSetNames = edition ? cType.getEditionMetadataSetNames(includeInternal) : cType.getViewMetadataSetNames(includeInternal); 793 for (String metadataSetName : metadataSetNames) 794 { 795 MetadataSet metadataSet = edition ? cType.getMetadataSetForEdition(metadataSetName) : cType.getMetadataSetForView(metadataSetName); 796 797 Map<String, Object> viewInfos = new HashMap<>(); 798 viewInfos.put("name", metadataSetName); 799 viewInfos.put("label", metadataSet.getLabel()); 800 viewInfos.put("description", metadataSet.getDescription()); 801 metadataSets.add(viewInfos); 802 } 803 804 return metadataSets; 805 } 806 807 /** 808 * Get the indexing model resulting of the concatenation of indexing models of given content types and mixins. 809 * @param content The content. 810 * @return The indexing model 811 */ 812 public IndexingModel getIndexingModel(Content content) 813 { 814 return getIndexingModel(content.getTypes(), content.getMixinTypes()); 815 } 816 817 /** 818 * Get the indexing model resulting of the concatenation of indexing models of given content types and mixins. 819 * @param cTypes The id of content types 820 * @param mixins The id of mixins 821 * @return The indexing model 822 */ 823 public IndexingModel getIndexingModel(String[] cTypes, String[] mixins) 824 { 825 IndexingModel joinIndexingModel = new IndexingModel(); 826 827 for (String id : cTypes) 828 { 829 ContentType cType = _getContentTypeEP().getExtension(id); 830 831 if (cType != null) 832 { 833 IndexingModel indexingModel = cType.getIndexingModel(); 834 835 for (IndexingField field : indexingModel.getFields()) 836 { 837 joinIndexingModel.addIndexingField(field); 838 } 839 } 840 else 841 { 842 if (getLogger().isWarnEnabled()) 843 { 844 getLogger().warn(String.format("Trying to get indexing model for an unknown content type : '%s'.", id)); 845 } 846 } 847 } 848 849 for (String id : mixins) 850 { 851 ContentType mixin = _getContentTypeEP().getExtension(id); 852 853 if (mixin != null) 854 { 855 IndexingModel indexingModel = mixin.getIndexingModel(); 856 857 for (IndexingField field : indexingModel.getFields()) 858 { 859 joinIndexingModel.addIndexingField(field); 860 } 861 } 862 else 863 { 864 if (getLogger().isWarnEnabled()) 865 { 866 getLogger().warn(String.format("Trying to get indexing model for an unknown mixin type : '%s'.", id)); 867 } 868 } 869 } 870 871 return joinIndexingModel; 872 } 873 874 875 /** 876 * Copy the elements of metadata set into a metadata set of destination, 877 * only if the elements are not already presents 878 * 879 * @param src The metadata set to copy 880 * @param dest The metadata of destination 881 * @deprecated Use {@link View#includeView(View)} instead 882 */ 883 @Deprecated 884 public void copyMetadataSetElementsIfNotExist(AbstractMetadataSetElement src, AbstractMetadataSetElement dest) 885 { 886 _copyMetadataSetElementsIfNotExist(src, dest, null); 887 } 888 889 /** 890 * Copy the elements of the source to the destination if they do not exist in the reference 891 * @param src the source 892 * @param dest the destination 893 * @param reference the reference 894 * @deprecated Use {@link View#includeView(View)} instead 895 */ 896 @Deprecated 897 private void _copyMetadataSetElementsIfNotExist(AbstractMetadataSetElement src, AbstractMetadataSetElement dest, AbstractMetadataSetElement reference) 898 { 899 AbstractMetadataSetElement root = reference == null ? dest : reference; 900 901 for (AbstractMetadataSetElement elmt : src.getElements()) 902 { 903 if (elmt instanceof MetadataDefinitionReference) 904 { 905 String metadataName = ((MetadataDefinitionReference) elmt).getMetadataName(); 906 if (!root.hasMetadataDefinitionReference(metadataName)) 907 { 908 dest.addElement(elmt); 909 } 910 } 911 else if (elmt instanceof Fieldset) 912 { 913 Fieldset fieldset = new Fieldset(); 914 fieldset.setLabel(((Fieldset) elmt).getLabel()); 915 fieldset.setRole(((Fieldset) elmt).getRole()); 916 917 _copyMetadataSetElementsIfNotExist(elmt, fieldset, root); 918 919 if (fieldset.getElements().size() > 0) 920 { 921 dest.addElement(fieldset); 922 } 923 } 924 } 925 } 926 927 /** 928 * Retrieve the definition of a given content metadata value path. 929 * @param metadataValuePath the metadata value path: it is separated by slashes and repeaters are valued: eg: "mycomposite/myrepeater[3]/mymetadata". 930 * @param content the content. 931 * @return the metadata definition or <code>null</code> if not found 932 * @deprecated Use {@link AttributeDefinition} API instead 933 */ 934 @Deprecated 935 public MetadataDefinition getMetadataDefinitionByMetadataValuePath(String metadataValuePath, Content content) 936 { 937 return getMetadataDefinitionByMetadataValuePath(metadataValuePath, content.getTypes(), content.getMixinTypes()); 938 } 939 940 /** 941 * Retrieve the definition of a given content metadata value path. 942 * @param metadataValuePath the metadata value path: it is separated by slashes and repeaters are valued: eg: "mycomposite.myrepeater[3].mymetadata". 943 * @param cTypes The id of content types 944 * @param mixins The id of mixins 945 * @return the metadata definition or <code>null</code> if not found 946 * @deprecated Use {@link AttributeDefinition} API instead 947 */ 948 @Deprecated 949 public MetadataDefinition getMetadataDefinitionByMetadataValuePath(String metadataValuePath, String[] cTypes, String[] mixins) 950 { 951 for (String id : cTypes) 952 { 953 ContentType cType = _getContentTypeEP().getExtension(id); 954 955 MetadataDefinition metadataDef = getMetadataDefinitionByMetadataValuePath(metadataValuePath, cType); 956 if (metadataDef != null) 957 { 958 return metadataDef; 959 } 960 } 961 962 for (String id : mixins) 963 { 964 ContentType cType = _getContentTypeEP().getExtension(id); 965 966 MetadataDefinition metadataDef = getMetadataDefinitionByMetadataValuePath(metadataValuePath, cType); 967 if (metadataDef != null) 968 { 969 return metadataDef; 970 } 971 } 972 973 return null; 974 } 975 976 /** 977 * Retrieve the definition of a given content metadata value path. 978 * @param metadataValuePath the metadata value path: it is separated by slashes and repeaters are valued: eg: "mycomposite/myrepeater[3]/mymetadata". 979 * @param metadataDefHolder The metadata def holder (such as the content type) 980 * @return the metadata definition or <code>null</code> if not found 981 * @deprecated Use {@link AttributeDefinition} API instead 982 */ 983 @Deprecated 984 private MetadataDefinition getMetadataDefinitionByMetadataValuePath(String metadataValuePath, MetadataDefinitionHolder metadataDefHolder) 985 { 986 String metadataName = StringUtils.substringBefore(metadataValuePath, ContentConstants.METADATA_PATH_SEPARATOR); 987 String subMetadataPath = StringUtils.substringAfter(metadataValuePath, ContentConstants.METADATA_PATH_SEPARATOR); 988 // Ignore the repeater entry if any (we're only browsing the model). 989 metadataName = StringUtils.substringBefore(metadataName, "["); 990 991 MetadataDefinition metadataDefinition = metadataDefHolder.getMetadataDefinition(metadataName); 992 if (metadataDefinition != null && StringUtils.isNotBlank(subMetadataPath)) 993 { 994 if (metadataDefinition.getType() == MetadataType.COMPOSITE) 995 { 996 return getMetadataDefinitionByMetadataValuePath(subMetadataPath, metadataDefinition); 997 } 998 else 999 { 1000 return null; 1001 } 1002 } 1003 1004 return metadataDefinition; 1005 } 1006 1007 /** 1008 * Get a flat map of MetadataDefinition corresponding to a metadataset of a content 1009 * @param metadataSet The metadataset to get 1010 * @return The flat map of metadatapath and MetadataDefinition 1011 * @deprecated Use {@link View} API instead 1012 */ 1013 @Deprecated 1014 public Set<String> getMetadataPaths(MetadataSet metadataSet) 1015 { 1016 Set<String> results = new HashSet<>(); 1017 1018 for (AbstractMetadataSetElement subMetadataSetElement : metadataSet.getElements()) 1019 { 1020 results.addAll(getMetadataPaths(subMetadataSetElement, "")); 1021 } 1022 1023 return results; 1024 } 1025 1026 /** 1027 * Get a flat map of metadatadefinition corresponding to a metadataset of a content 1028 * @param metadataSet The metadataset to get 1029 * @param prefix The metadata path of the parent 1030 * @return The flat map of metadatapath and metadatadefinition 1031 * @deprecated Use {@link ViewItem} API instead 1032 */ 1033 @Deprecated 1034 protected Set<String> getMetadataPaths(AbstractMetadataSetElement metadataSet, String prefix) 1035 { 1036 Set<String> results = new HashSet<>(); 1037 1038 MetadataDefinitionReference metadataDefRef = (MetadataDefinitionReference) metadataSet; 1039 1040 String metadataName = metadataDefRef.getMetadataName(); 1041 1042 String metadataPath = prefix + metadataName; 1043 results.add(metadataPath); 1044 1045 for (AbstractMetadataSetElement subMetadataSetElement : metadataSet.getElements()) 1046 { 1047 results.addAll(getMetadataPaths(subMetadataSetElement, metadataPath + ContentConstants.METADATA_PATH_SEPARATOR)); 1048 } 1049 1050 return results; 1051 } 1052 1053 /** 1054 * Retrieve the metadata definition represented by the given path. 1055 * The path can represent a metadata on another content. 1056 * @param metadataPath the metadata path separated by '/' 1057 * @param content The content. 1058 * @return the metadata definition or null. 1059 * @deprecated Use {@link Content#getDefinition(String)} API instead 1060 */ 1061 @Deprecated 1062 public MetadataDefinition getMetadataDefinition(String metadataPath, Content content) 1063 { 1064 List<MetadataDefinition> metadataDefinitionsByPath = getMetadataDefinitionPath(metadataPath, content); 1065 return metadataDefinitionsByPath.size() > 0 ? metadataDefinitionsByPath.get(metadataDefinitionsByPath.size() - 1) : null; 1066 } 1067 1068 /** 1069 * Get a flat map of MetadataDefinition corresponding to a set of metadata paths 1070 * @param metadataPaths A set of metadata path to analyse 1071 * @param content The content associated 1072 * @return The flat map of metadatapath and MetadataDefinition 1073 * @deprecated Use {@link AttributeDefinition} API instead 1074 */ 1075 @Deprecated 1076 public Map<String, MetadataDefinition> getMetadataDefinitions(Set<String> metadataPaths, Content content) 1077 { 1078 Map<String, MetadataDefinition> results = new HashMap<>(); 1079 1080 for (String metadataPath : metadataPaths) 1081 { 1082 results.put(metadataPath, getMetadataDefinition(metadataPath, content)); 1083 } 1084 1085 return results; 1086 } 1087 1088 /** 1089 * Get a flat map of MetadataDefinition corresponding to a metadataSet 1090 * @param metadataSet A metadataSet to analyse 1091 * @param content The content associated 1092 * @return The flat map of metadatapath and MetadataDefinition 1093 * @deprecated Use {@link AttributeDefinition} API instead 1094 */ 1095 @Deprecated 1096 public Map<String, MetadataDefinition> getMetadataDefinitions(MetadataSet metadataSet, Content content) 1097 { 1098 return getMetadataDefinitions(getMetadataPaths(metadataSet), content); 1099 } 1100 1101 /** 1102 * Retrieve the list of successive metadata definitions represented by the given path. 1103 * The path can represent a metadata on another content. 1104 * @param metadataPath the metadata path separated by '/' 1105 * @param content The content. 1106 * @return the list of metadata definitions, one by path element. 1107 * @deprecated Use {@link AttributeDefinition} API instead 1108 */ 1109 @Deprecated 1110 public List<MetadataDefinition> getMetadataDefinitionPath(String metadataPath, Content content) 1111 { 1112 return getMetadataDefinitionPath(metadataPath, content.getTypes(), content.getMixinTypes()); 1113 } 1114 1115 /** 1116 * Get a flat map of MetadataDefinition path corresponding to a set of metadata paths 1117 * @param metadataPaths A set of metadata path to analyse 1118 * @param content The content associated 1119 * @return The flat map of metadatapath and corresponding list of MetadataDefinition path 1120 * @deprecated Use {@link AttributeDefinition} API instead 1121 */ 1122 @Deprecated 1123 public Map<String, List<MetadataDefinition>> getMetadataDefinitionsPaths(Set<String> metadataPaths, Content content) 1124 { 1125 Map<String, List<MetadataDefinition>> results = new HashMap<>(); 1126 1127 for (String metadataPath : metadataPaths) 1128 { 1129 results.put(metadataPath, getMetadataDefinitionPath(metadataPath, content)); 1130 } 1131 1132 return results; 1133 } 1134 1135 /** 1136 * Get a flat map of MetadataDefinition path corresponding to a metadataset 1137 * @param metadataSet A metadataset to analyse 1138 * @param content The content associated 1139 * @return The flat map of metadatapath and corresponding list of MetadataDefinition path 1140 * @deprecated Use {@link AttributeDefinition} API instead 1141 */ 1142 @Deprecated 1143 public Map<String, List<MetadataDefinition>> getMetadataDefinitionsPaths(MetadataSet metadataSet, Content content) 1144 { 1145 return getMetadataDefinitionsPaths(getMetadataPaths(metadataSet), content); 1146 } 1147 1148 /** 1149 * Retrieve the metadata definition represented by the given path. 1150 * The path can represent a metadata on another content. 1151 * @param metadataPath the metadata path separated by '/' 1152 * @param cTypes The content types. 1153 * @param mixins The content mixins types. 1154 * @return the metadata definition or null. 1155 * @deprecated Use {@link AttributeDefinition} API instead 1156 */ 1157 @Deprecated 1158 public MetadataDefinition getMetadataDefinition(String metadataPath, String[] cTypes, String[] mixins) 1159 { 1160 List<MetadataDefinition> metadataDefinitionsByPath = getMetadataDefinitionPath(metadataPath, cTypes, mixins); 1161 return metadataDefinitionsByPath.size() > 0 ? metadataDefinitionsByPath.get(metadataDefinitionsByPath.size() - 1) : null; 1162 } 1163 1164 /** 1165 * Get a flat map of MetadataDefinition corresponding to a set of metadata paths 1166 * @param metadataPaths A set of metadata path to analyse 1167 * @param cTypes The content types. 1168 * @param mixins The content mixins types. 1169 * @return The flat map of metadatapath and MetadataDefinition 1170 * @deprecated Use {@link AttributeDefinition} API instead 1171 */ 1172 @Deprecated 1173 public Map<String, MetadataDefinition> getMetadataDefinitions(Set<String> metadataPaths, String[] cTypes, String[] mixins) 1174 { 1175 Map<String, MetadataDefinition> results = new HashMap<>(); 1176 1177 for (String metadataPath : metadataPaths) 1178 { 1179 results.put(metadataPath, getMetadataDefinition(metadataPath, cTypes, mixins)); 1180 } 1181 1182 return results; 1183 } 1184 1185 /** 1186 * Get a flat map of MetadataDefinition corresponding to a metadataset 1187 * @param metadataSet A metadataset to analyse 1188 * @param cTypes The content types. 1189 * @param mixins The content mixins types. 1190 * @return The flat map of metadatapath and MetadataDefinition 1191 * @deprecated Use {@link AttributeDefinition} API instead 1192 */ 1193 @Deprecated 1194 public Map<String, MetadataDefinition> getMetadataDefinitions(MetadataSet metadataSet, String[] cTypes, String[] mixins) 1195 { 1196 return getMetadataDefinitions(getMetadataPaths(metadataSet), cTypes, mixins); 1197 } 1198 1199 /** 1200 * Get a flat map of MetadataDefinition corresponding to the required predicate (boolean-valued function) 1201 * For example, to get all metadata definitions of type CONTENT, we can do: 1202 * getMetadataDefinitions(contentType, m -> m.getType() == MetadataType.CONTENT) 1203 * 1204 * @param metaDefHolder The metadata definition holder 1205 * @param predicate The predicate (ex. m -> m.getType() == MetadataType.CONTENT) 1206 * @return The flat map of metadata's path and their definition 1207 * @deprecated Use {@link AttributeDefinition} API instead 1208 */ 1209 @Deprecated 1210 public Map<String, MetadataDefinition> getMetadataDefinitions(MetadataDefinitionHolder metaDefHolder, Predicate<MetadataDefinition> predicate) 1211 { 1212 Map<String, MetadataDefinition> metaDefs = new HashMap<>(); 1213 1214 Set<String> metadataNames = metaDefHolder.getMetadataNames(); 1215 for (String metadataName : metadataNames) 1216 { 1217 MetadataDefinition metadataDef = metaDefHolder.getMetadataDefinition(metadataName); 1218 if (predicate.test(metadataDef)) 1219 { 1220 metaDefs.put(metadataDef.getId(), metadataDef); 1221 } 1222 1223 if (metadataDef.getType() == MetadataType.COMPOSITE || metadataDef instanceof RepeaterDefinition) 1224 { 1225 metaDefs.putAll(getMetadataDefinitions(metadataDef, predicate)); 1226 } 1227 } 1228 1229 return metaDefs; 1230 } 1231 1232 /** 1233 * Retrieve the list of successive metadata definitions represented by the given path. 1234 * The path can represent a metadata on another content. 1235 * @param metadataPath the metadata path separated by '/' 1236 * @param cTypes The id of content types 1237 * @param mixins The id of mixins 1238 * @return the metadata definition or <code>null</code> if not found 1239 * @deprecated Use {@link AttributeDefinition} API instead 1240 */ 1241 @Deprecated 1242 public List<MetadataDefinition> getMetadataDefinitionPath(String metadataPath, String[] cTypes, String[] mixins) 1243 { 1244 String[] allContentTypes = ArrayUtils.addAll(cTypes, mixins); 1245 1246 for (String cTypeId : allContentTypes) 1247 { 1248 ContentType cType = _getContentTypeEP().getExtension(cTypeId); 1249 if (cType != null) 1250 { 1251 List<MetadataDefinition> metaDefs = getMetadataDefinitionPath(metadataPath, cType); 1252 if (!metaDefs.isEmpty()) 1253 { 1254 return metaDefs; 1255 } 1256 } 1257 else 1258 { 1259 if (getLogger().isWarnEnabled()) 1260 { 1261 getLogger().warn("Unknown content type identifier : " + cTypeId); 1262 } 1263 } 1264 } 1265 1266 return Collections.emptyList(); 1267 } 1268 1269 /** 1270 * Get a flat map of MetadataDefinition path corresponding to a set of metadata paths 1271 * @param metadataPaths A set of metadata path to analyse 1272 * @param cTypes The id of content types 1273 * @param mixins The id of mixins 1274 * @return The flat map of metadatapath and corresponding list of MetadataDefinition path 1275 * @deprecated Use {@link AttributeDefinition} API instead 1276 */ 1277 @Deprecated 1278 public Map<String, List<MetadataDefinition>> getMetadataDefinitionsPaths(Set<String> metadataPaths, String[] cTypes, String[] mixins) 1279 { 1280 Map<String, List<MetadataDefinition>> results = new HashMap<>(); 1281 1282 for (String metadataPath : metadataPaths) 1283 { 1284 results.put(metadataPath, getMetadataDefinitionPath(metadataPath, cTypes, mixins)); 1285 } 1286 1287 return results; 1288 } 1289 1290 /** 1291 * Get a flat map of MetadataDefinition path corresponding to a metadataset 1292 * @param metadataSet A metadataset to analyse 1293 * @param cTypes The id of content types 1294 * @param mixins The id of mixins 1295 * @return The flat map of metadatapath and corresponding list of MetadataDefinition path 1296 * @deprecated Use {@link AttributeDefinition} API instead 1297 */ 1298 @Deprecated 1299 public Map<String, List<MetadataDefinition>> getMetadataDefinitionsPaths(MetadataSet metadataSet, String[] cTypes, String[] mixins) 1300 { 1301 return getMetadataDefinitionsPaths(getMetadataPaths(metadataSet), cTypes, mixins); 1302 } 1303 1304 /** 1305 * Retrieves a metadata definition from a path. The metadata can be defined in a referenced or sub content. 1306 * @param metadataPath the metadata path separated by '/' 1307 * @param initialContentType The initial content type to start the search 1308 * @return the metadata definition or <code>null</code> if not found 1309 * @deprecated Use {@link ContentType#getModelItem(String)} instead 1310 */ 1311 @Deprecated 1312 public MetadataDefinition getMetadataDefinition(String metadataPath, ContentType initialContentType) 1313 { 1314 List<MetadataDefinition> metadataDefinitionsByPath = getMetadataDefinitionPath(metadataPath, initialContentType); 1315 return metadataDefinitionsByPath.size() > 0 ? metadataDefinitionsByPath.get(metadataDefinitionsByPath.size() - 1) : null; 1316 } 1317 1318 /** 1319 * Get a flat map of MetadataDefinition corresponding to a set of metadata paths 1320 * @param metadataPaths A set of metadata path to analyse 1321 * @param initialContentType The initial content type to start the search 1322 * @return The flat map of metadatapath and MetadataDefinition 1323 * @deprecated Use {@link AttributeDefinition} API instead 1324 */ 1325 @Deprecated 1326 public Map<String, MetadataDefinition> getMetadataDefinitions(Set<String> metadataPaths, ContentType initialContentType) 1327 { 1328 Map<String, MetadataDefinition> results = new HashMap<>(); 1329 1330 for (String metadataPath : metadataPaths) 1331 { 1332 results.put(metadataPath, getMetadataDefinition(metadataPath, initialContentType)); 1333 } 1334 1335 return results; 1336 } 1337 1338 /** 1339 * Get a flat map of MetadataDefinition corresponding to a metadataset 1340 * @param metadataSet A metadataset to analyse 1341 * @param initialContentType The initial content type to start the search 1342 * @return The flat map of metadatapath and MetadataDefinition 1343 * @deprecated Use {@link AttributeDefinition} API instead 1344 */ 1345 @Deprecated 1346 public Map<String, MetadataDefinition> getMetadataDefinitions(MetadataSet metadataSet, ContentType initialContentType) 1347 { 1348 return getMetadataDefinitions(getMetadataPaths(metadataSet), initialContentType); 1349 } 1350 1351 /** 1352 * Retrieve the list of successive metadata definitions represented by the given path. 1353 * The path can represent a metadata on another content. 1354 * @param initialContentType The initial content type to start the search 1355 * @param metadataPath the metadata path separated by '/' 1356 * @return the list of metadata definitions, one by path element. 1357 * @deprecated Use {@link ContentType#getModelItem(String)} API instead 1358 */ 1359 @Deprecated 1360 public List<MetadataDefinition> getMetadataDefinitionPath(String metadataPath, ContentType initialContentType) 1361 { 1362 List<MetadataDefinition> definitions = new ArrayList<>(); 1363 1364 String[] pathSegments = StringUtils.split(metadataPath, ContentConstants.METADATA_PATH_SEPARATOR); 1365 1366 if (pathSegments.length > 0) 1367 { 1368 MetadataDefinition metadataDef = initialContentType.getMetadataDefinition(pathSegments[0]); 1369 1370 if (metadataDef != null) 1371 { 1372 definitions.add(metadataDef); 1373 } 1374 else 1375 { 1376 return Collections.emptyList(); 1377 } 1378 1379 for (int i = 1; i < pathSegments.length; i++) 1380 { 1381 if (metadataDef.getType() == MetadataType.CONTENT || metadataDef.getType() == MetadataType.SUB_CONTENT) 1382 { 1383 String refCTypeId = metadataDef.getContentType(); 1384 if (refCTypeId != null && _getContentTypeEP().hasExtension(refCTypeId)) 1385 { 1386 ContentType refCType = _getContentTypeEP().getExtension(refCTypeId); 1387 1388 List<MetadataDefinition> followingDefs = getMetadataDefinitionPath(StringUtils.join(pathSegments, ContentConstants.METADATA_PATH_SEPARATOR, i, pathSegments.length), refCType); 1389 if (CollectionUtils.isEmpty(followingDefs)) 1390 { 1391 return Collections.emptyList(); 1392 } 1393 definitions.addAll(followingDefs); 1394 1395 return definitions; 1396 } 1397 else if ("title".equals(pathSegments[i])) 1398 { 1399 // No specific content type: allow only title. 1400 definitions.add(getTitleMetadataDefinition()); 1401 return definitions; 1402 } 1403 else 1404 { 1405 return Collections.emptyList(); 1406 } 1407 } 1408 else 1409 { 1410 metadataDef = metadataDef.getMetadataDefinition(pathSegments[i]); 1411 if (metadataDef != null) 1412 { 1413 definitions.add(metadataDef); 1414 } 1415 else 1416 { 1417 return Collections.emptyList(); 1418 } 1419 } 1420 } 1421 } 1422 1423 return definitions; 1424 } 1425 1426 /** 1427 * Get a flat map of MetadataDefinition path corresponding to a set of metadata paths 1428 * @param metadataPaths A set of metadata path to analyse 1429 * @param initialContentType The initial content type to start the search 1430 * @return The flat map of metadatapath and corresponding list of MetadataDefinition path 1431 * @deprecated Use {@link AttributeDefinition} API instead 1432 */ 1433 @Deprecated 1434 public Map<String, List<MetadataDefinition>> getMetadataDefinitionsPaths(Set<String> metadataPaths, ContentType initialContentType) 1435 { 1436 Map<String, List<MetadataDefinition>> results = new HashMap<>(); 1437 1438 for (String metadataPath : metadataPaths) 1439 { 1440 results.put(metadataPath, getMetadataDefinitionPath(metadataPath, initialContentType)); 1441 } 1442 1443 return results; 1444 } 1445 1446 /** 1447 * Get a flat map of MetadataDefinition path corresponding to a metadataSet 1448 * @param metadataSet A semetadataSet to analyse 1449 * @param initialContentType The initial content type to start the search 1450 * @return The flat map of metadatapath and corresponding list of MetadataDefinition path 1451 * @deprecated Use {@link AttributeDefinition} API instead 1452 */ 1453 @Deprecated 1454 public Map<String, List<MetadataDefinition>> getMetadataDefinitionsPaths(MetadataSet metadataSet, ContentType initialContentType) 1455 { 1456 return getMetadataDefinitionsPaths(getMetadataPaths(metadataSet), initialContentType); 1457 } 1458 1459 /** 1460 * Determines if the given content type can be added to content 1461 * 1462 * @param content The content 1463 * @param cTypeId The id of content type 1464 * @return <code>true</code> if the content type is compatible with content 1465 */ 1466 public boolean isCompatibleContentType(Content content, String cTypeId) 1467 { 1468 String[] currentContentTypes = ArrayUtils.addAll(content.getTypes(), content.getMixinTypes()); 1469 1470 ArrayList<String> cTypes = new ArrayList<>(Arrays.asList(currentContentTypes)); 1471 cTypes.add(cTypeId); 1472 1473 try 1474 { 1475 getMetadataDefinitions(cTypes.toArray(new String[cTypes.size()])); 1476 return true; 1477 } 1478 catch (ConfigurationException e) 1479 { 1480 return false; 1481 } 1482 } 1483 1484 /** 1485 * Retrieves all definitions of a metadata resulting of the concatenation of 1486 * metadata of given content types. 1487 * 1488 * @param cTypes The id of content types 1489 * @return the metadata definitions 1490 * @throws ConfigurationException if an error occurred 1491 * @deprecated Use {@link AttributeDefinition} API instead 1492 */ 1493 @Deprecated 1494 public Map<String, MetadataDefinition> getMetadataDefinitions(String[] cTypes) throws ConfigurationException 1495 { 1496 Map<String, MetadataDefinition> metadata = new LinkedHashMap<>(); 1497 1498 for (String id : cTypes) 1499 { 1500 ContentType cType = _getContentTypeEP().getExtension(id); 1501 1502 for (String name : cType.getMetadataNames()) 1503 { 1504 MetadataDefinition definition = cType.getMetadataDefinition(name); 1505 1506 if (metadata.containsKey(name)) 1507 { 1508 if (!definition.getReferenceContentType().equals(metadata.get(name).getReferenceContentType())) 1509 { 1510 // The definition does not provide from a common ancestor 1511 throw new ConfigurationException("The metadata '" + name + "' defined in content-type '" + id + "' is already defined in another co-super-type '" 1512 + metadata.get(name).getReferenceContentType() + "'"); 1513 } 1514 continue; 1515 } 1516 1517 metadata.put(name, definition); 1518 } 1519 } 1520 1521 return metadata; 1522 } 1523 1524 /** 1525 * Retrieves all model items of given content types. 1526 * @param contentTypes The identifier of the content types 1527 * @return the model items 1528 * @throws ConfigurationException if an error occurred 1529 */ 1530 public Map<String, ModelItem> getModelItems(String[] contentTypes) throws ConfigurationException 1531 { 1532 Map<String, ModelItem> items = new LinkedHashMap<>(); 1533 1534 for (String id : contentTypes) 1535 { 1536 ContentType contentType = _getContentTypeEP().getExtension(id); 1537 1538 for (ModelItem currentItem : contentType.getModelItems()) 1539 { 1540 final String currentItemName = currentItem.getName(); 1541 if (items.containsKey(currentItemName)) 1542 { 1543 ModelItem existingItem = items.get(currentItemName); 1544 1545 if (!currentItem.getModel().equals(existingItem.getModel())) 1546 { 1547 // The definition does not provide from a common ancestor 1548 throw new ConfigurationException("The metadata '" + currentItemName + "' defined in content-type '" + id + "' is already defined in another co-super-type '" 1549 + existingItem.getModel() + "'"); 1550 } 1551 continue; 1552 } 1553 1554 items.put(currentItemName, currentItem); 1555 } 1556 } 1557 1558 return items; 1559 } 1560 1561 /** 1562 * Retrieves the common metadata definitions for a list of content types 1563 * @param cTypeIds The list of content types to consider 1564 * @param metadataSetName The metadata set name to list metadata 1565 * @param isEdition Is the metadata set for edition (or for view) 1566 * @return The map of metadata definition. Key are the metadata path in the content type 1567 * @deprecated Use {@link AttributeDefinition} API instead 1568 */ 1569 @Deprecated 1570 public Map<String, MetadataDefinition> getCommonMetadataDefinitions(Collection<String> cTypeIds, String metadataSetName, boolean isEdition) 1571 { 1572 Map<String, MetadataDefinition> commonMetadataDefinitions = null; 1573 1574 for (String cTypeId : cTypeIds) 1575 { 1576 ContentType cType = _getContentTypeEP().getExtension(cTypeId); 1577 AbstractMetadataSetElement metadataSetElement = _getMetadataSet(cType, metadataSetName, isEdition); 1578 1579 Map<String, MetadataDefinition> accumulator = new LinkedHashMap<>(); 1580 if (metadataSetElement != null) 1581 { 1582 _getMetadataDefinitionsAcc(accumulator, metadataSetElement, "", cType); 1583 } 1584 1585 if (commonMetadataDefinitions == null) 1586 { 1587 commonMetadataDefinitions = new LinkedHashMap<>(); 1588 for (String name : accumulator.keySet()) 1589 { 1590 commonMetadataDefinitions.put(name, accumulator.get(name)); 1591 } 1592 } 1593 else 1594 { 1595 // only retains common metadata (performs a set intersection) 1596 commonMetadataDefinitions.keySet().retainAll(accumulator.keySet()); 1597 } 1598 } 1599 1600 return commonMetadataDefinitions != null ? commonMetadataDefinitions : Collections.emptyMap(); 1601 } 1602 1603 /** 1604 * Populate the accumulator with the metadata definition 1605 * @param internalAcc the accumulator of metadata definition 1606 * @param metadataSetElement the metadata set of the content 1607 * @param prefix the path prefix preceding the names of the metadata 1608 * @param metaDefHolder the holder of the metadata definitions for the content 1609 * @deprecated Use {@link AttributeDefinition} API instead 1610 */ 1611 @Deprecated 1612 protected void _getMetadataDefinitionsAcc(Map<String, MetadataDefinition> internalAcc, AbstractMetadataSetElement metadataSetElement, String prefix, MetadataDefinitionHolder metaDefHolder) 1613 { 1614 if (metadataSetElement instanceof MetadataDefinitionReference) 1615 { 1616 MetadataDefinitionReference metadataDefRef = (MetadataDefinitionReference) metadataSetElement; 1617 String subMetadataName = metadataDefRef.getMetadataName(); 1618 MetadataDefinition subMetadataDef = metaDefHolder.getMetadataDefinition(subMetadataName); 1619 1620 if (subMetadataDef != null) 1621 { 1622 if (MetadataType.COMPOSITE.equals(subMetadataDef.getType())) 1623 { 1624 for (AbstractMetadataSetElement subElement : metadataSetElement.getElements()) 1625 { 1626 String path = prefix + subMetadataName; 1627 internalAcc.put(path, subMetadataDef); 1628 _getMetadataDefinitionsAcc(internalAcc, subElement, path + "/", subMetadataDef); 1629 } 1630 } 1631 else 1632 { 1633 String path = prefix + subMetadataName; 1634 internalAcc.put(path, subMetadataDef); 1635 } 1636 } 1637 } 1638 else 1639 { 1640 for (AbstractMetadataSetElement subElement : metadataSetElement.getElements()) 1641 { 1642 _getMetadataDefinitionsAcc(internalAcc, subElement, prefix, metaDefHolder); 1643 } 1644 } 1645 } 1646 1647 /** 1648 * Get the metadataset of a content given the parameters 1649 * @param contentType The content type to consider 1650 * @param metadataSetName The metadata set to get (that list the metadata to consider) 1651 * @param isEdition Is the metadata set for edition (or for view) 1652 * @return The metadataset 1653 * @deprecated Use {@link AttributeDefinition} API instead 1654 */ 1655 @Deprecated 1656 protected MetadataSet _getMetadataSet(ContentType contentType, String metadataSetName, boolean isEdition) 1657 { 1658 String name = metadataSetName; 1659 if (StringUtils.isBlank(name)) 1660 { 1661 name = "main"; 1662 } 1663 1664 MetadataSet metadataSet = null; 1665 1666 if (isEdition) 1667 { 1668 metadataSet = contentType.getMetadataSetForEdition(name); 1669 } 1670 else 1671 { 1672 metadataSet = contentType.getMetadataSetForView(name); 1673 } 1674 1675 return metadataSet; 1676 } 1677 1678 /** 1679 * Get information on content types.<br> 1680 * @return A Map with content types 1681 */ 1682 public Set<Map<String, Object>> getContentTypesInformations() 1683 { 1684 Set<Map<String, Object>> result = new HashSet<>(); 1685 1686 // Collect content types. 1687 Collection<String> allContentTypesIds = _getContentTypeEP().getExtensionsIds(); 1688 for (String id : allContentTypesIds) 1689 { 1690 ContentType cType = _getContentTypeEP().getExtension(id); 1691 1692 if (cType != null) 1693 { 1694 Map<String, Object> contentTypeProperties = getContentTypeProperties(cType); 1695 contentTypeProperties.put("rightEvaluated", _hasRight(cType)); 1696 result.add(contentTypeProperties); 1697 } 1698 } 1699 1700 return result; 1701 } 1702 1703 /** 1704 * Get information on content types.<br> 1705 * 1706 * @param ids The id of content types to retrieve 1707 * @param inherited If true, the sub-types will be also returned. 1708 * @param checkRights If true, only content types allowed for creation will 1709 * be returned 1710 * @param includePrivate If true, the list will include the private content types. By default the list is restricted to the public content types. 1711 * @param includeMixins If true the list will include the mixins content types. 1712 * @param includeAbstract If true the list will include the abstract content types. 1713 * @return A Map with retrieved, unknown, not-allowed and private content types 1714 */ 1715 @Callable 1716 public Map<String, Object> getContentTypesList (List<String> ids, boolean inherited, boolean checkRights, boolean includePrivate, boolean includeMixins, boolean includeAbstract) 1717 { 1718 // Collect content types. 1719 Collection<String> allContentTypesIds = new ArrayList<>(); 1720 if (ids == null || ids.isEmpty()) 1721 { 1722 allContentTypesIds = _getContentTypeEP().getExtensionsIds(); 1723 } 1724 else 1725 { 1726 for (String id : ids) 1727 { 1728 _addIfNotPresent(allContentTypesIds, id); 1729 1730 if (inherited) 1731 { 1732 for (String subTypeId : _getContentTypeEP().getSubTypes(id)) 1733 { 1734 _addIfNotPresent(allContentTypesIds, subTypeId); 1735 } 1736 } 1737 } 1738 } 1739 1740 // Resolve and organize content types 1741 return _dispatchContentTypes(checkRights, includePrivate, includeMixins, includeAbstract, allContentTypesIds); 1742 } 1743 1744 private Map<String, Object> _dispatchContentTypes(boolean checkRights, boolean includePrivate, boolean includeMixins, boolean includeAbstract, Collection<String> allContentTypesIds) 1745 { 1746 List<Map<String, Object>> contentTypes = new ArrayList<>(); 1747 List<String> unknownContentTypes = new ArrayList<>(); 1748 List<String> noRightContentTypes = new ArrayList<>(); 1749 List<String> privateContentTypes = new ArrayList<>(); 1750 List<String> mixinContentTypes = new ArrayList<>(); 1751 List<String> abstractContentTypes = new ArrayList<>(); 1752 1753 for (String id : allContentTypesIds) 1754 { 1755 ContentType cType = _getContentTypeEP().getExtension(id); 1756 1757 if (cType != null) 1758 { 1759 if (cType.isAbstract() && !includeAbstract) 1760 { 1761 abstractContentTypes.add(id); 1762 } 1763 else if (cType.isPrivate() && !includePrivate) 1764 { 1765 privateContentTypes.add(id); 1766 } 1767 else if (cType.isMixin() && !includeMixins) 1768 { 1769 mixinContentTypes.add(id); 1770 } 1771 else if (!checkRights || _hasRight(cType)) 1772 { 1773 contentTypes.add(getContentTypeProperties(cType)); 1774 } 1775 else 1776 { 1777 noRightContentTypes.add(id); 1778 } 1779 } 1780 else 1781 { 1782 unknownContentTypes.add(id); 1783 } 1784 } 1785 1786 Map<String, Object> result = new HashMap<>(); 1787 1788 result.put("contentTypes", contentTypes); 1789 result.put("noRightContentTypes", noRightContentTypes); 1790 result.put("unknownContentTypes", unknownContentTypes); 1791 result.put("privateContentTypes", privateContentTypes); 1792 result.put("mixinContentTypes", mixinContentTypes); 1793 result.put("abstractContentTypes", abstractContentTypes); 1794 1795 return result; 1796 } 1797 1798 /** 1799 * Get the content type properties 1800 * 1801 * @param contentType The content type 1802 * @return The content type properties 1803 */ 1804 public Map<String, Object> getContentTypeProperties(ContentType contentType) 1805 { 1806 Map<String, Object> infos = new HashMap<>(); 1807 1808 infos.put("id", contentType.getId()); 1809 infos.put("label", contentType.getLabel()); 1810 infos.put("description", contentType.getDescription()); 1811 infos.put("defaultTitle", contentType.getDefaultTitle()); 1812 infos.put("iconGlyph", contentType.getIconGlyph()); 1813 infos.put("iconDecorator", contentType.getIconDecorator()); 1814 infos.put("iconSmall", contentType.getSmallIcon()); 1815 infos.put("iconMedium", contentType.getMediumIcon()); 1816 infos.put("iconLarge", contentType.getLargeIcon()); 1817 infos.put("right", contentType.getRight()); 1818 infos.put("isMultilingual", contentType.isMultilingual()); 1819 infos.put("isSimple", contentType.isSimple()); 1820 infos.put("isPrivate", contentType.isPrivate()); 1821 infos.put("isAbstract", contentType.isAbstract()); 1822 infos.put("isReferenceTable", contentType.isReferenceTable()); 1823 infos.put("isMixin", contentType.isMixin()); 1824 infos.put("superTypes", contentType.getSupertypeIds()); 1825 infos.put("tags", contentType.getTags()); 1826 infos.put("parentMetadataName", Optional.ofNullable(contentType.getParentMetadata()).map(MetadataDefinition::getId).orElse("")); 1827 1828 return infos; 1829 } 1830 1831 /** 1832 * Test if the current user has the right needed by the content type to create a content. 1833 * @param contentType The content type 1834 * @return true if the user has the right needed, false otherwise. 1835 */ 1836 protected boolean _hasRight(ContentType contentType) 1837 { 1838 boolean hasRight = false; 1839 1840 String right = contentType.getRight(); 1841 1842 if (right == null) 1843 { 1844 hasRight = true; 1845 } 1846 else 1847 { 1848 UserIdentity user = _userProvider.getUser(); 1849 hasRight = _rightManager.hasRight(user, right, "/cms") == RightResult.RIGHT_ALLOW || _rightManager.hasRight(user, right, _rootContentHelper.getRootContent()) == RightResult.RIGHT_ALLOW; 1850 } 1851 1852 return hasRight; 1853 } 1854 1855 private void _addIfNotPresent(Collection<String> collection, String value) 1856 { 1857 if (!collection.contains(value)) 1858 { 1859 collection.add(value); 1860 } 1861 } 1862 1863 /** 1864 * Determine whether a metadata can be read at this time. 1865 * 1866 * @param metadataDef the metadata definition 1867 * @param content The content where metadata is to be read on. 1868 * @return <code>true</code> if the current user is allowed to read the 1869 * metadata of this content. 1870 * @throws AmetysRepositoryException if an error occurs while accessing the 1871 * content. 1872 * @deprecated Use {@link AttributeDefinition} API instead 1873 */ 1874 @Deprecated 1875 public boolean canRead(Content content, MetadataDefinition metadataDef) 1876 { 1877 String id = metadataDef.getReferenceContentType(); 1878 ContentType cType = _getContentTypeEP().getExtension(id); 1879 return cType.canRead(content, metadataDef); 1880 } 1881 1882 /** 1883 * Determine whether a metadata can be read at this time. 1884 * 1885 * @param metadataDef the metadata definition 1886 * @param content The content where metadata is to be read on. 1887 * @return <code>true</code> if the current user is allowed to read the 1888 * metadata of this content. 1889 * @throws AmetysRepositoryException if an error occurs while accessing the 1890 * content. 1891 * @deprecated Use {@link AttributeDefinition} API instead 1892 */ 1893 @Deprecated 1894 public boolean canWrite(Content content, MetadataDefinition metadataDef) 1895 { 1896 String id = metadataDef.getReferenceContentType(); 1897 ContentType cType = _getContentTypeEP().getExtension(id); 1898 return cType.canWrite(content, metadataDef); 1899 } 1900 1901 /** 1902 * Get the id of content type to use for rendering 1903 * 1904 * @param content The content 1905 * @return the id of dynamic or standard content type 1906 */ 1907 public String getContentTypeIdForRendering(Content content) 1908 { 1909 String dynamicContentTypeId = getDynamicContentTypeId(content.getTypes(), content.getMixinTypes()); 1910 if (dynamicContentTypeId != null) 1911 { 1912 return dynamicContentTypeId; 1913 } 1914 1915 ContentType firstContentType = getFirstContentType(content); 1916 return firstContentType != null ? firstContentType.getId() : StringUtils.EMPTY; 1917 } 1918 1919 /** 1920 * Get the id of the dynamic content type matching to given ones 1921 * @param contentTypes The content types 1922 * @param mixinTypes The mixins 1923 * @return the id of dynamic content type or null if no dynamic content type was found 1924 */ 1925 public String getDynamicContentTypeId(String[] contentTypes, String[] mixinTypes) 1926 { 1927 DynamicContentTypeDescriptor dynamicContentType = _getDynamicContentTypeDescriptorExtentionPoint().getMatchingDescriptor(contentTypes, mixinTypes); 1928 if (dynamicContentType != null) 1929 { 1930 return dynamicContentType.getId(); 1931 } 1932 1933 return null; 1934 } 1935 1936 /** 1937 * Get the plugin name of content type to use for rendering 1938 * 1939 * @param content The content 1940 * @return the plugin name of dynamic or standard content type 1941 */ 1942 public String getContentTypePluginForRendering(Content content) 1943 { 1944 String dynamicContentTypePlugin = getDynamicContentTypePlugin(content.getTypes(), content.getMixinTypes()); 1945 if (dynamicContentTypePlugin != null) 1946 { 1947 return dynamicContentTypePlugin; 1948 } 1949 1950 ContentType firstContentType = getFirstContentType(content); 1951 return firstContentType != null ? firstContentType.getPluginName() : StringUtils.EMPTY; 1952 } 1953 1954 /** 1955 * Get the plugin name of the dynamic content type matching to given ones 1956 * @param contentTypes The content types 1957 * @param mixinTypes The mixins 1958 * @return the plugin name of dynamic content type or null if no dynamic content type was found 1959 */ 1960 public String getDynamicContentTypePlugin(String[] contentTypes, String[] mixinTypes) 1961 { 1962 DynamicContentTypeDescriptor dynamicContentType = _getDynamicContentTypeDescriptorExtentionPoint().getMatchingDescriptor(contentTypes, mixinTypes); 1963 if (dynamicContentType != null) 1964 { 1965 return dynamicContentType.getPluginName(); 1966 } 1967 1968 return null; 1969 } 1970 1971 /** 1972 * Retrieves the label of the content type. 1973 * 1974 * @param content The content 1975 * @return the label. 1976 */ 1977 public I18nizableText getContentTypeLabel(Content content) 1978 { 1979 DynamicContentTypeDescriptor dynamicContentType = _getDynamicContentTypeDescriptorExtentionPoint().getMatchingDescriptor(content.getTypes(), content.getMixinTypes()); 1980 if (dynamicContentType != null) 1981 { 1982 return dynamicContentType.getLabel(); 1983 } 1984 1985 ContentType firstContentType = getFirstContentType(content); 1986 return firstContentType != null ? firstContentType.getLabel() : new I18nizableText(StringUtils.EMPTY); 1987 } 1988 1989 /** 1990 * Retrieves the description of the content type. 1991 * 1992 * @param content The content 1993 * @return the label. 1994 */ 1995 public I18nizableText getContentTypeDescription(Content content) 1996 { 1997 DynamicContentTypeDescriptor dynamicContentType = _getDynamicContentTypeDescriptorExtentionPoint().getMatchingDescriptor(content.getTypes(), content.getMixinTypes()); 1998 if (dynamicContentType != null) 1999 { 2000 return dynamicContentType.getDescription(); 2001 } 2002 2003 ContentType firstContentType = getFirstContentType(content); 2004 return firstContentType != null ? firstContentType.getDescription() : new I18nizableText(StringUtils.EMPTY); 2005 } 2006 2007 /** 2008 * Retrieves the default title of the content type. 2009 * 2010 * @param content The content 2011 * @return the label. 2012 */ 2013 public I18nizableText getContentTypeDefaultTitle(Content content) 2014 { 2015 DynamicContentTypeDescriptor dynamicContentType = _getDynamicContentTypeDescriptorExtentionPoint().getMatchingDescriptor(content.getTypes(), content.getMixinTypes()); 2016 if (dynamicContentType != null) 2017 { 2018 return dynamicContentType.getDefaultTitle(); 2019 } 2020 2021 ContentType firstContentType = getFirstContentType(content); 2022 return firstContentType != null ? firstContentType.getDefaultTitle() : new I18nizableText(StringUtils.EMPTY); 2023 } 2024 2025 /** 2026 * Retrieves the category of the content type. 2027 * 2028 * @param content The content 2029 * @return the label. 2030 */ 2031 public I18nizableText getContentTypeCategory(Content content) 2032 { 2033 DynamicContentTypeDescriptor dynamicCTDescriptor = _getDynamicContentTypeDescriptorExtentionPoint().getMatchingDescriptor(content.getTypes(), content.getMixinTypes()); 2034 if (dynamicCTDescriptor != null) 2035 { 2036 return dynamicCTDescriptor.getCategory(); 2037 } 2038 2039 ContentType firstContentType = getFirstContentType(content); 2040 return firstContentType != null ? firstContentType.getCategory() : new I18nizableText(StringUtils.EMPTY); 2041 } 2042 2043 /** 2044 * Retrieves the CSS class to use as glyph icon of the content 2045 * @param content The content 2046 * @return the glyph 2047 */ 2048 public String getIconGlyph(Content content) 2049 { 2050 DynamicContentTypeDescriptor dynamicCTDescriptor = _getDynamicContentTypeDescriptorExtentionPoint().getMatchingDescriptor(content.getTypes(), content.getMixinTypes()); 2051 if (dynamicCTDescriptor != null) 2052 { 2053 return dynamicCTDescriptor.getIconGlyph(); 2054 } 2055 2056 ContentType firstContentType = getFirstContentType(content); 2057 return firstContentType != null ? firstContentType.getIconGlyph() : null; 2058 } 2059 2060 /** 2061 * Retrieves the CSS class to use as decorator above the main icon 2062 * @param content The content 2063 * @return the decorator CSS class name 2064 */ 2065 public String getIconDecorator(Content content) 2066 { 2067 DynamicContentTypeDescriptor dynamicCTDescriptor = _getDynamicContentTypeDescriptorExtentionPoint().getMatchingDescriptor(content.getTypes(), content.getMixinTypes()); 2068 if (dynamicCTDescriptor != null) 2069 { 2070 return dynamicCTDescriptor.getIconDecorator(); 2071 } 2072 2073 ContentType firstContentType = getFirstContentType(content); 2074 return firstContentType != null ? firstContentType.getIconDecorator() : null; 2075 } 2076 2077 /** 2078 * Retrieves the URL of the icon without the context path. 2079 * 2080 * @param content The content 2081 * @return the icon URL for the small image 16x16. 2082 */ 2083 public String getSmallIcon(Content content) 2084 { 2085 DynamicContentTypeDescriptor dynamicCTDescriptor = _getDynamicContentTypeDescriptorExtentionPoint().getMatchingDescriptor(content.getTypes(), content.getMixinTypes()); 2086 if (dynamicCTDescriptor != null) 2087 { 2088 return dynamicCTDescriptor.getSmallIcon(); 2089 } 2090 2091 ContentType firstContentType = getFirstContentType(content); 2092 return firstContentType != null ? firstContentType.getSmallIcon() : StringUtils.EMPTY; 2093 } 2094 2095 /** 2096 * Retrieves the URL of the icon without the context path. 2097 * 2098 * @param content The content 2099 * @return the icon URL for the medium image 32x32. 2100 */ 2101 public String getMediumIcon(Content content) 2102 { 2103 DynamicContentTypeDescriptor dynamicCTDescriptor = _getDynamicContentTypeDescriptorExtentionPoint().getMatchingDescriptor(content.getTypes(), content.getMixinTypes()); 2104 if (dynamicCTDescriptor != null) 2105 { 2106 return dynamicCTDescriptor.getMediumIcon(); 2107 } 2108 2109 ContentType firstContentType = getFirstContentType(content); 2110 return firstContentType != null ? firstContentType.getMediumIcon() : StringUtils.EMPTY; 2111 } 2112 2113 /** 2114 * Retrieves the URL of the icon without the context path. 2115 * 2116 * @param content The content 2117 * @return the icon URL for the large image 48x48. 2118 */ 2119 public String getLargeIcon(Content content) 2120 { 2121 DynamicContentTypeDescriptor dynamicCTDescriptor = _getDynamicContentTypeDescriptorExtentionPoint().getMatchingDescriptor(content.getTypes(), content.getMixinTypes()); 2122 if (dynamicCTDescriptor != null) 2123 { 2124 return dynamicCTDescriptor.getLargeIcon(); 2125 } 2126 2127 ContentType firstContentType = getFirstContentType(content); 2128 return firstContentType != null ? firstContentType.getLargeIcon() : StringUtils.EMPTY; 2129 } 2130 2131 /** 2132 * Get the content type which determines the content icons and rendering 2133 * 2134 * @param content The content 2135 * @return The main content type 2136 */ 2137 public ContentType getFirstContentType(Content content) 2138 { 2139 TreeSet<ContentType> treeSet = new TreeSet<>(new ContentTypeComparator()); 2140 2141 for (String id : content.getTypes()) 2142 { 2143 ContentType contentType = _getContentTypeEP().getExtension(id); 2144 if (contentType != null) 2145 { 2146 treeSet.add(contentType); 2147 } 2148 else 2149 { 2150 if (getLogger().isWarnEnabled()) 2151 { 2152 getLogger().warn(String.format("Trying to get an unknown content type : '%s'.", id)); 2153 } 2154 } 2155 } 2156 2157 return !treeSet.isEmpty() ? treeSet.first() : null; 2158 } 2159 2160 /** 2161 * Get the metadata definition for the "title" standard metadata. 2162 * @return The standard title metadata definition. 2163 * @deprecated Use {@link AttributeDefinition} API instead 2164 */ 2165 @Deprecated 2166 public static MetadataDefinition getTitleMetadataDefinition() 2167 { 2168 MetadataDefinition def = new MetadataDefinition(); 2169 def.setId("title"); 2170 def.setName("title"); 2171 def.setLabel(new I18nizableText("plugin.cms", "PLUGINS_CMS_METADATA_TITLE_LABEL")); 2172 def.setDescription(new I18nizableText("plugin.cms", "PLUGINS_CMS_METADATA_TITLE_DESCRIPTION")); 2173 def.setType(MetadataType.STRING); 2174 def.setMultiple(false); 2175 2176 return def; 2177 } 2178 2179 class ContentTypeComparator implements Comparator<ContentType> 2180 { 2181 @Override 2182 public int compare(ContentType c1, ContentType c2) 2183 { 2184 I18nizableText t1 = c1.getLabel(); 2185 I18nizableText t2 = c2.getLabel(); 2186 2187 String str1 = t1.isI18n() ? t1.getKey() : t1.getLabel(); 2188 String str2 = t2.isI18n() ? t2.getKey() : t2.getLabel(); 2189 2190 int compareTo = str1.toString().compareTo(str2.toString()); 2191 if (compareTo == 0) 2192 { 2193 // Content types have same keys but there are not equals, so do 2194 // not return 0 to add it in TreeSet 2195 // Indeed, in a TreeSet implementation two elements that are 2196 // equal by the method compareTo are, from the standpoint of the 2197 // set, equal 2198 return 1; 2199 } 2200 return compareTo; 2201 } 2202 } 2203 2204 private String _getCacheIdentifier(String[] contentTypes, String[] mixins) 2205 { 2206 String[] sortedContentTypes = contentTypes.clone(); 2207 Arrays.sort(sortedContentTypes); 2208 String name = StringUtils.join(sortedContentTypes, ";"); 2209 2210 if (mixins.length > 0) 2211 { 2212 String[] sortedMixins = mixins.clone(); 2213 Arrays.sort(sortedMixins); 2214 name += ";"; 2215 name += StringUtils.join(mixins, ";"); 2216 } 2217 2218 return name; 2219 } 2220}