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