001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (C) Alkacon Software (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ade.configuration; 029 030import org.opencms.ade.configuration.formatters.CmsFormatterBeanParser; 031import org.opencms.ade.configuration.formatters.CmsFormatterChangeSet; 032import org.opencms.ade.configuration.formatters.CmsFormatterConfigurationCacheState; 033import org.opencms.ade.containerpage.shared.CmsContainer; 034import org.opencms.ade.detailpage.CmsDetailPageInfo; 035import org.opencms.file.CmsObject; 036import org.opencms.file.CmsResource; 037import org.opencms.file.CmsResourceFilter; 038import org.opencms.file.types.CmsResourceTypeFolder; 039import org.opencms.file.types.CmsResourceTypeFunctionConfig; 040import org.opencms.file.types.CmsResourceTypeXmlContent; 041import org.opencms.file.types.I_CmsResourceType; 042import org.opencms.gwt.CmsIconUtil; 043import org.opencms.jsp.util.CmsFunctionRenderer; 044import org.opencms.loader.CmsLoaderException; 045import org.opencms.main.CmsException; 046import org.opencms.main.CmsLog; 047import org.opencms.main.OpenCms; 048import org.opencms.util.CmsStringUtil; 049import org.opencms.util.CmsUUID; 050import org.opencms.xml.CmsXmlContentDefinition; 051import org.opencms.xml.containerpage.CmsFormatterConfiguration; 052import org.opencms.xml.containerpage.CmsXmlDynamicFunctionHandler; 053import org.opencms.xml.containerpage.I_CmsFormatterBean; 054import org.opencms.xml.content.CmsXmlContentFactory; 055import org.opencms.xml.content.CmsXmlContentProperty; 056 057import java.util.ArrayList; 058import java.util.Collection; 059import java.util.Collections; 060import java.util.HashMap; 061import java.util.HashSet; 062import java.util.Iterator; 063import java.util.LinkedHashMap; 064import java.util.List; 065import java.util.Map; 066import java.util.Set; 067 068import org.apache.commons.lang3.builder.ReflectionToStringBuilder; 069import org.apache.commons.logging.Log; 070 071import com.google.common.base.Optional; 072import com.google.common.collect.Lists; 073import com.google.common.collect.Maps; 074import com.google.common.collect.Sets; 075 076/** 077 * A class which represents the accessible configuration data at a given point in a sitemap.<p> 078 */ 079public class CmsADEConfigData { 080 081 /** 082 * Bean which contains the detail information for a single sub-sitemap and resource type.<p> 083 * 084 * This includes both information about the detail page itself, as well as the path of the folder 085 * which is used to store that content type in this subsitemap.<p> 086 * 087 */ 088 public class DetailInfo { 089 090 /** The base path of the sitemap configuration where this information originates from. */ 091 private String m_basePath; 092 093 /** The information about the detail page info itself. */ 094 private CmsDetailPageInfo m_detailPageInfo; 095 096 /** The content folder path. */ 097 private String m_folderPath; 098 099 /** The detail type. */ 100 private String m_type; 101 102 /** 103 * Creates a new instance.<p> 104 * 105 * @param folderPath the content folder path 106 * @param detailPageInfo the detail page information 107 * @param type the detail type 108 * @param basePath the base path of the sitemap configuration 109 */ 110 public DetailInfo(String folderPath, CmsDetailPageInfo detailPageInfo, String type, String basePath) { 111 112 m_folderPath = folderPath; 113 m_detailPageInfo = detailPageInfo; 114 m_type = type; 115 m_basePath = basePath; 116 117 } 118 119 /** 120 * Gets the base path of the sitemap configuration from which this information is coming.<p> 121 * 122 * @return the base path 123 */ 124 public String getBasePath() { 125 126 return m_basePath; 127 } 128 129 /** 130 * Gets the detail page information.<p> 131 * 132 * @return the detail page information 133 */ 134 public CmsDetailPageInfo getDetailPageInfo() { 135 136 return m_detailPageInfo; 137 } 138 139 /** 140 * Gets the content folder path.<p> 141 * 142 * @return the content folder path 143 */ 144 public String getFolderPath() { 145 146 return m_folderPath; 147 } 148 149 /** 150 * Gets the detail type.<p> 151 * 152 * @return the detail type 153 */ 154 public String getType() { 155 156 return m_type; 157 } 158 159 /** 160 * Sets the base path.<p> 161 * 162 * @param basePath the new base path value 163 */ 164 public void setBasePath(String basePath) { 165 166 m_basePath = basePath; 167 } 168 169 /** 170 * @see java.lang.Object#toString() 171 */ 172 @Override 173 public String toString() { 174 175 return ReflectionToStringBuilder.toString(this); 176 } 177 } 178 179 /** The log instance for this class. */ 180 private static final Log LOG = CmsLog.getLog(CmsADEConfigData.class); 181 182 /** The wrapped configuration bean containing the actual data. */ 183 protected CmsADEConfigDataInternal m_data; 184 185 /** The cache state to which the wrapped configuration bean belongs. */ 186 private CmsADEConfigCacheState m_cache; 187 188 /** The configuration sequence (contains the list of all sitemap configuration data beans to be used for inheritance). */ 189 private CmsADEConfigurationSequence m_configSequence; 190 191 /** 192 * Creates a new configuration data object, based on an internal configuration data bean and a 193 * configuration cache state.<p> 194 * 195 * @param data the internal configuration data bean 196 * @param cache the configuration cache state 197 * @param configSequence the configuration sequence 198 */ 199 public CmsADEConfigData( 200 CmsADEConfigDataInternal data, 201 CmsADEConfigCacheState cache, 202 CmsADEConfigurationSequence configSequence) { 203 204 m_data = data; 205 m_cache = cache; 206 m_configSequence = configSequence; 207 } 208 209 /** 210 * Generic method to merge lists of named configuration objects.<p> 211 * 212 * The lists are merged such that the configuration objects from the child list rise to the front of the result list, 213 * and two configuration objects will be merged themselves if they share the same name.<p> 214 * 215 * For example, if we have two lists of configuration objects:<p> 216 * 217 * parent: A1, B1, C1<p> 218 * child: D2, B2<p> 219 * 220 * then the resulting list will look like:<p> 221 * 222 * D2, B3, A1, C1<p> 223 * 224 * where B3 is the result of merging B1 and B2.<p> 225 * 226 * @param <C> the type of configuration object 227 * @param parentConfigs the parent configurations 228 * @param childConfigs the child configurations 229 * @param preserveDisabled if true, try to merge parents with disabled children instead of discarding them 230 * 231 * @return the merged configuration object list 232 */ 233 public static <C extends I_CmsConfigurationObject<C>> List<C> combineConfigurationElements( 234 List<C> parentConfigs, 235 List<C> childConfigs, 236 boolean preserveDisabled) { 237 238 List<C> result = new ArrayList<C>(); 239 Map<String, C> map = new LinkedHashMap<String, C>(); 240 if (parentConfigs != null) { 241 for (C parent : Lists.reverse(parentConfigs)) { 242 map.put(parent.getKey(), parent); 243 } 244 } 245 if (childConfigs == null) { 246 childConfigs = Collections.emptyList(); 247 } 248 for (C child : Lists.reverse(childConfigs)) { 249 String childKey = child.getKey(); 250 if (child.isDisabled() && !preserveDisabled) { 251 map.remove(childKey); 252 } else { 253 C parent = map.get(childKey); 254 map.remove(childKey); 255 C newValue; 256 if (parent != null) { 257 newValue = parent.merge(child); 258 } else { 259 newValue = child; 260 } 261 map.put(childKey, newValue); 262 } 263 } 264 result = new ArrayList<C>(map.values()); 265 Collections.reverse(result); 266 // those multiple "reverse" calls may a bit confusing. They are there because on the one hand we want to keep the 267 // configuration items from one configuration in the same order as they are defined, on the other hand we want 268 // configuration items from a child configuration to rise to the top of the configuration items. 269 270 // so for example, if the parent configuration has items with the keys A,B,C,E 271 // and the child configuration has items with the keys C,B,D 272 // we want the items of the combined configuration in the order C,B,D,A,E 273 274 return result; 275 } 276 277 /** 278 * Applies the formatter change sets of this and all parent configurations to a map of external (non-schema) formatters.<p> 279 * 280 * @param formatters the external formatter map which will be modified 281 * 282 * @param formatterCacheState the formatter cache state from which new external formatters should be fetched 283 */ 284 public void applyAllFormatterChanges( 285 Map<CmsUUID, I_CmsFormatterBean> formatters, 286 CmsFormatterConfigurationCacheState formatterCacheState) { 287 288 for (CmsFormatterChangeSet changeSet : getFormatterChangeSets()) { 289 changeSet.applyToFormatters(formatters, formatterCacheState); 290 } 291 } 292 293 /** 294 * Gets the active external (non-schema) formatters for this sub-sitemap.<p> 295 * 296 * @return the map of active external formatters by structure id 297 */ 298 public Map<CmsUUID, I_CmsFormatterBean> getActiveFormatters() { 299 300 CmsFormatterConfigurationCacheState cacheState = getCachedFormatters(); 301 Map<CmsUUID, I_CmsFormatterBean> result = Maps.newHashMap(cacheState.getAutoEnabledFormatters()); 302 applyAllFormatterChanges(result, cacheState); 303 return result; 304 } 305 306 /** 307 * Gets the list of all detail pages.<p> 308 * 309 * @return the list of all detail pages 310 */ 311 public List<CmsDetailPageInfo> getAllDetailPages() { 312 313 return getAllDetailPages(true); 314 } 315 316 /** 317 * Gets a list of all detail pages.<p> 318 * 319 * @param update if true, this method will try to correct the root paths in the returned objects if the corresponding resources have been moved 320 * 321 * @return the list of all detail pages 322 */ 323 public List<CmsDetailPageInfo> getAllDetailPages(boolean update) { 324 325 CmsADEConfigData parentData = parent(); 326 List<CmsDetailPageInfo> parentDetailPages; 327 if (parentData != null) { 328 parentDetailPages = parentData.getAllDetailPages(false); 329 } else { 330 parentDetailPages = Collections.emptyList(); 331 } 332 List<CmsDetailPageInfo> result = mergeDetailPages(parentDetailPages, m_data.getOwnDetailPages()); 333 if (update) { 334 result = updateUris(result); 335 } 336 return result; 337 } 338 339 /** 340 * Gets the configuration base path.<p> 341 * 342 * For example, if the configuration file is located at /sites/default/.content/.config, the base path is /sites/default.<p> 343 * 344 * @return the base path of the configuration 345 */ 346 public String getBasePath() { 347 348 return m_data.getBasePath(); 349 } 350 351 /** 352 * Gets the cached formatters.<p> 353 * 354 * @return the cached formatters 355 */ 356 public CmsFormatterConfigurationCacheState getCachedFormatters() { 357 358 return OpenCms.getADEManager().getCachedFormatters( 359 getCms().getRequestContext().getCurrentProject().isOnlineProject()); 360 } 361 362 /** 363 * Returns the names of the bundles configured as workplace bundles in any module configuration.<p> 364 * 365 * @return the names of the bundles configured as workplace bundles in any module configuration. 366 */ 367 public Set<String> getConfiguredWorkplaceBundles() { 368 369 Set<String> result = new HashSet<String>(); 370 for (CmsResourceTypeConfig config : internalGetResourceTypes(false)) { 371 String bundlename = config.getConfiguredWorkplaceBundle(); 372 if (null != bundlename) { 373 result.add(bundlename); 374 } 375 } 376 return result; 377 } 378 379 /** 380 * Gets the content folder path.<p> 381 * 382 * For example, if the configuration file is located at /sites/default/.content/.config, the content folder path is /sites/default/.content 383 * 384 * @return the content folder path 385 */ 386 public String getContentFolderPath() { 387 388 return CmsStringUtil.joinPaths(m_data.getBasePath(), CmsADEManager.CONTENT_FOLDER_NAME); 389 390 } 391 392 /** 393 * Returns a list of the creatable resource types.<p> 394 * 395 * @param cms the CMS context used to check whether the resource types are creatable 396 * @param pageFolderRootPath the root path of the current container page 397 * @return the list of creatable resource type 398 * 399 * @throws CmsException if something goes wrong 400 */ 401 public List<CmsResourceTypeConfig> getCreatableTypes(CmsObject cms, String pageFolderRootPath) throws CmsException { 402 403 List<CmsResourceTypeConfig> result = new ArrayList<CmsResourceTypeConfig>(); 404 for (CmsResourceTypeConfig typeConfig : getResourceTypes()) { 405 if (typeConfig.checkCreatable(cms, pageFolderRootPath)) { 406 result.add(typeConfig); 407 } 408 } 409 return result; 410 } 411 412 /** 413 * Returns the default detail page.<p> 414 * 415 * @return the default detail page 416 */ 417 public CmsDetailPageInfo getDefaultDetailPage() { 418 419 for (CmsDetailPageInfo detailpage : getAllDetailPages(true)) { 420 if (CmsADEManager.DEFAULT_DETAILPAGE_TYPE.equals(detailpage.getType())) { 421 return detailpage; 422 } 423 } 424 return null; 425 } 426 427 /** 428 * Returns the default model page.<p> 429 * 430 * @return the default model page 431 */ 432 public CmsModelPageConfig getDefaultModelPage() { 433 434 List<CmsModelPageConfig> modelPages = getModelPages(); 435 for (CmsModelPageConfig modelPageConfig : getModelPages()) { 436 if (modelPageConfig.isDefault()) { 437 return modelPageConfig; 438 } 439 } 440 if (modelPages.isEmpty()) { 441 return null; 442 } 443 return modelPages.get(0); 444 } 445 446 /** 447 * Gets the detail information for this sitemap config data object.<p> 448 * 449 * @param cms the CMS context 450 * @return the list of detail information 451 */ 452 public List<DetailInfo> getDetailInfos(CmsObject cms) { 453 454 List<DetailInfo> result = Lists.newArrayList(); 455 List<CmsDetailPageInfo> detailPages = getAllDetailPages(true); 456 Collections.reverse(detailPages); // make sure primary detail pages come later in the list and override other detail pages for the same type 457 Map<String, CmsDetailPageInfo> primaryDetailPageMapByType = Maps.newHashMap(); 458 for (CmsDetailPageInfo pageInfo : detailPages) { 459 primaryDetailPageMapByType.put(pageInfo.getType(), pageInfo); 460 } 461 for (CmsResourceTypeConfig typeConfig : getResourceTypes()) { 462 String typeName = typeConfig.getTypeName(); 463 if (((typeConfig.getFolderOrName() == null) || !typeConfig.getFolderOrName().isPageRelative()) 464 && primaryDetailPageMapByType.containsKey(typeName)) { 465 String folderPath = typeConfig.getFolderPath(cms, null); 466 CmsDetailPageInfo pageInfo = primaryDetailPageMapByType.get(typeName); 467 result.add(new DetailInfo(folderPath, pageInfo, typeName, getBasePath())); 468 } 469 } 470 return result; 471 } 472 473 /** 474 * Gets the detail pages for a specific type.<p> 475 * 476 * @param type the type name 477 * 478 * @return the list of detail pages for that type 479 */ 480 public List<CmsDetailPageInfo> getDetailPagesForType(String type) { 481 482 List<CmsDetailPageInfo> result = new ArrayList<CmsDetailPageInfo>(); 483 CmsResourceTypeConfig typeConfig = getResourceType(type); 484 if (type.startsWith(CmsDetailPageInfo.FUNCTION_PREFIX) 485 || ((typeConfig != null) && !typeConfig.isDetailPagesDisabled())) { 486 487 CmsDetailPageInfo defaultPage = null; 488 for (CmsDetailPageInfo detailpage : getAllDetailPages(true)) { 489 if (detailpage.getType().equals(type)) { 490 result.add(detailpage); 491 } else if ((defaultPage == null) 492 && CmsADEManager.DEFAULT_DETAILPAGE_TYPE.equals(detailpage.getType())) { 493 defaultPage = detailpage; 494 } 495 } 496 if (defaultPage != null) { 497 // add default detail page last 498 result.add(defaultPage); 499 } 500 } 501 return result; 502 } 503 504 /** 505 * Returns all available display formatters.<p> 506 * 507 * @param cms the cms context 508 * 509 * @return the available display formatters 510 */ 511 public List<I_CmsFormatterBean> getDisplayFormatters(CmsObject cms) { 512 513 List<I_CmsFormatterBean> result = new ArrayList<I_CmsFormatterBean>(); 514 515 for (I_CmsFormatterBean formatter : getCachedFormatters().getFormatters().values()) { 516 if (formatter.isDisplayFormatter()) { 517 result.add(formatter); 518 } 519 } 520 return result; 521 } 522 523 /** 524 * Returns the restricted dynamic functions or <code>null</code>.<p> 525 * 526 * @return the dynamic functions 527 */ 528 public Collection<CmsUUID> getDynamicFunctions() { 529 530 CmsADEConfigData parentData = parent(); 531 Collection<CmsUUID> result = m_data.getDynamicFunctions(); 532 if ((result == null) && (parentData != null)) { 533 result = parentData.getDynamicFunctions(); 534 } 535 return result; 536 } 537 538 /** 539 * Returns the formatter change sets for this and all parent sitemaps, ordered by increasing folder depth of the sitemap.<p> 540 * 541 * @return the formatter change sets for all ancestor sitemaps 542 */ 543 public List<CmsFormatterChangeSet> getFormatterChangeSets() { 544 545 CmsADEConfigData currentConfig = this; 546 List<CmsFormatterChangeSet> result = Lists.newArrayList(); 547 while (currentConfig != null) { 548 CmsFormatterChangeSet changes = currentConfig.getOwnFormatterChangeSet(); 549 if (changes != null) { 550 result.add(changes); 551 } 552 currentConfig = currentConfig.parent(); 553 } 554 Collections.reverse(result); 555 return result; 556 } 557 558 /** 559 * Gets the formatter configuration for a resource.<p> 560 * 561 * @param cms the current CMS context 562 * @param res the resource for which the formatter configuration should be retrieved 563 * 564 * @return the configuration of formatters for the resource 565 */ 566 public CmsFormatterConfiguration getFormatters(CmsObject cms, CmsResource res) { 567 568 if (CmsResourceTypeFunctionConfig.isFunction(res)) { 569 570 CmsFormatterConfigurationCacheState formatters = getCachedFormatters(); 571 I_CmsFormatterBean function = formatters.getFormatters().get(res.getStructureId()); 572 if (function != null) { 573 return CmsFormatterConfiguration.create(cms, Collections.singletonList(function)); 574 } else { 575 if ((!res.getStructureId().isNullUUID()) 576 && cms.existsResource(res.getStructureId(), CmsResourceFilter.IGNORE_EXPIRATION)) { 577 // usually if it's just been created, but not added to the configuration cache yet 578 CmsFormatterBeanParser parser = new CmsFormatterBeanParser(cms, new HashMap<>()); 579 try { 580 function = parser.parse( 581 CmsXmlContentFactory.unmarshal(cms, cms.readFile(res)), 582 res.getRootPath(), 583 "" + res.getStructureId()); 584 return CmsFormatterConfiguration.create(cms, Collections.singletonList(function)); 585 } catch (Exception e) { 586 LOG.warn(e.getLocalizedMessage(), e); 587 return CmsFormatterConfiguration.EMPTY_CONFIGURATION; 588 } 589 590 } else { 591 // if a new function has been dragged on the page, it doesn't exist in the VFS yet, so we need a different 592 // instance as a replacement 593 CmsResource defaultFormatter = CmsFunctionRenderer.getDefaultFunctionInstance(cms); 594 if (defaultFormatter != null) { 595 I_CmsFormatterBean defaultFormatterBean = formatters.getFormatters().get( 596 defaultFormatter.getStructureId()); 597 return CmsFormatterConfiguration.create(cms, Collections.singletonList(defaultFormatterBean)); 598 } else { 599 LOG.warn("Could not read default formatter for functions."); 600 return CmsFormatterConfiguration.EMPTY_CONFIGURATION; 601 } 602 } 603 } 604 } else { 605 try { 606 int resTypeId = res.getTypeId(); 607 return getFormatters( 608 cms, 609 OpenCms.getResourceManager().getResourceType(resTypeId), 610 getFormattersFromSchema(cms, res)); 611 } catch (CmsLoaderException e) { 612 LOG.warn(e.getLocalizedMessage(), e); 613 return CmsFormatterConfiguration.EMPTY_CONFIGURATION; 614 } 615 } 616 } 617 618 /** 619 * Gets a named function reference.<p> 620 * 621 * @param name the name of the function reference 622 * 623 * @return the function reference for the given name 624 */ 625 public CmsFunctionReference getFunctionReference(String name) { 626 627 List<CmsFunctionReference> functionReferences = getFunctionReferences(); 628 for (CmsFunctionReference functionRef : functionReferences) { 629 if (functionRef.getName().equals(name)) { 630 return functionRef; 631 } 632 } 633 return null; 634 } 635 636 /** 637 * Gets the list of configured function references.<p> 638 * 639 * @return the list of configured function references 640 */ 641 public List<CmsFunctionReference> getFunctionReferences() { 642 643 return internalGetFunctionReferences(); 644 } 645 646 /** 647 * Gets the map of external (non-schema) formatters which are inactive in this sub-sitemap.<p> 648 * 649 * @return the map inactive external formatters 650 */ 651 public Map<CmsUUID, I_CmsFormatterBean> getInactiveFormatters() { 652 653 CmsFormatterConfigurationCacheState cacheState = getCachedFormatters(); 654 Map<CmsUUID, I_CmsFormatterBean> result = Maps.newHashMap(cacheState.getFormatters()); 655 result.keySet().removeAll(getActiveFormatters().keySet()); 656 return result; 657 } 658 659 /** 660 * Gets the main detail page for a specific type.<p> 661 * 662 * @param type the type name 663 * 664 * @return the main detail page for that type 665 */ 666 public CmsDetailPageInfo getMainDetailPage(String type) { 667 668 List<CmsDetailPageInfo> detailPages = getDetailPagesForType(type); 669 if ((detailPages == null) || detailPages.isEmpty()) { 670 return null; 671 } 672 return detailPages.get(0); 673 } 674 675 /** 676 * Gets the list of available model pages.<p> 677 * 678 * @return the list of available model pages 679 */ 680 public List<CmsModelPageConfig> getModelPages() { 681 682 return getModelPages(false); 683 } 684 685 /** 686 * Gets the list of available model pages.<p> 687 * 688 * @param includeDisable <code>true</code> to include disabled model pages 689 * 690 * @return the list of available model pages 691 */ 692 public List<CmsModelPageConfig> getModelPages(boolean includeDisable) { 693 694 CmsADEConfigData parentData = parent(); 695 List<CmsModelPageConfig> parentModelPages; 696 if ((parentData != null) && !m_data.isDiscardInheritedModelPages()) { 697 parentModelPages = parentData.getModelPages(); 698 } else { 699 parentModelPages = Collections.emptyList(); 700 } 701 702 List<CmsModelPageConfig> result = combineConfigurationElements( 703 parentModelPages, 704 m_data.getOwnModelPageConfig(), 705 includeDisable); 706 return result; 707 } 708 709 /** 710 * Gets the formatter changes for this sitemap configuration.<p> 711 * 712 * @return the formatter change set 713 */ 714 public CmsFormatterChangeSet getOwnFormatterChangeSet() { 715 716 return m_data.getFormatterChangeSet(); 717 } 718 719 /** 720 * Gets the configuration for the available properties.<p> 721 * 722 * @return the configuration for the available properties 723 */ 724 public List<CmsPropertyConfig> getPropertyConfiguration() { 725 726 CmsADEConfigData parentData = parent(); 727 List<CmsPropertyConfig> parentProperties; 728 if ((parentData != null) && !m_data.isDiscardInheritedProperties()) { 729 parentProperties = parentData.getPropertyConfiguration(); 730 } else { 731 parentProperties = Collections.emptyList(); 732 } 733 List<CmsPropertyConfig> result = combineConfigurationElements( 734 parentProperties, 735 m_data.getOwnPropertyConfigurations(), 736 false); 737 return result; 738 } 739 740 /** 741 * Gets the property configuration as a map of CmsXmlContentProperty instances.<p> 742 * 743 * @return the map of property configurations 744 */ 745 public Map<String, CmsXmlContentProperty> getPropertyConfigurationAsMap() { 746 747 Map<String, CmsXmlContentProperty> result = new LinkedHashMap<String, CmsXmlContentProperty>(); 748 for (CmsPropertyConfig propConf : getPropertyConfiguration()) { 749 result.put(propConf.getName(), propConf.getPropertyData()); 750 } 751 return result; 752 } 753 754 /** 755 * Returns the resource from which this configuration was read.<p> 756 * 757 * @return the resource from which this configuration was read 758 */ 759 public CmsResource getResource() { 760 761 return m_data.getResource(); 762 } 763 764 /** 765 * Returns the configuration for a specific resource type.<p> 766 * 767 * @param typeName the name of the type 768 * 769 * @return the resource type configuration for that type 770 */ 771 public CmsResourceTypeConfig getResourceType(String typeName) { 772 773 for (CmsResourceTypeConfig type : getResourceTypes()) { 774 if (typeName.equals(type.getTypeName())) { 775 return type; 776 } 777 } 778 return null; 779 } 780 781 /** 782 * Gets a list of all available resource type configurations.<p> 783 * 784 * @return the available resource type configurations 785 */ 786 public List<CmsResourceTypeConfig> getResourceTypes() { 787 788 List<CmsResourceTypeConfig> result = internalGetResourceTypes(true); 789 for (CmsResourceTypeConfig config : result) { 790 config.initialize(getCms()); 791 } 792 return result; 793 } 794 795 /** 796 * Gets the searchable resource type configurations.<p> 797 * 798 * @param cms the current CMS context 799 * @return the searchable resource type configurations 800 */ 801 public Collection<CmsResourceTypeConfig> getSearchableTypes(CmsObject cms) { 802 803 return getResourceTypes(); 804 } 805 806 /** 807 * Gets the set of resource type names for which schema formatters can be enabled or disabled and which are not disabled in this sub-sitemap.<p> 808 * 809 * @return the set of types for which schema formatters are active 810 */ 811 public Set<String> getTypesWithActiveSchemaFormatters() { 812 813 Set<String> result = Sets.newHashSet(getTypesWithModifiableFormatters()); 814 for (CmsFormatterChangeSet changeSet : getFormatterChangeSets()) { 815 changeSet.applyToTypes(result); 816 } 817 return result; 818 } 819 820 /** 821 * Gets the set of names of resource types which have schema-based formatters that can be enabled or disabled.<p> 822 * 823 * @return the set of names of resource types which have schema-based formatters that can be enabled or disabled 824 */ 825 public Set<String> getTypesWithModifiableFormatters() { 826 827 Set<String> result = new HashSet<String>(); 828 for (I_CmsResourceType type : OpenCms.getResourceManager().getResourceTypes()) { 829 if (type instanceof CmsResourceTypeXmlContent) { 830 CmsXmlContentDefinition contentDef = null; 831 try { 832 contentDef = CmsXmlContentDefinition.getContentDefinitionForType(getCms(), type.getTypeName()); 833 if ((contentDef != null) && contentDef.getContentHandler().hasModifiableFormatters()) { 834 result.add(type.getTypeName()); 835 } 836 } catch (Exception e) { 837 LOG.error(e.getLocalizedMessage(), e); 838 } 839 } 840 } 841 return result; 842 843 } 844 845 /** 846 * Checks if there are any matching formatters for the given set of containers.<p> 847 * 848 * @param cms the current CMS context 849 * @param resType the resource type for which the formatter configuration should be retrieved 850 * @param containers the page containers 851 * 852 * @return if there are any matching formatters 853 */ 854 public boolean hasFormatters(CmsObject cms, I_CmsResourceType resType, Collection<CmsContainer> containers) { 855 856 try { 857 if (CmsXmlDynamicFunctionHandler.TYPE_FUNCTION.equals(resType.getTypeName()) 858 || CmsResourceTypeFunctionConfig.TYPE_NAME.equals(resType.getTypeName())) { 859 // dynamic function may match any container 860 return true; 861 } 862 CmsXmlContentDefinition def = CmsXmlContentDefinition.getContentDefinitionForType( 863 cms, 864 resType.getTypeName()); 865 CmsFormatterConfiguration schemaFormatters = def.getContentHandler().getFormatterConfiguration(cms, null); 866 CmsFormatterConfiguration formatters = getFormatters(cms, resType, schemaFormatters); 867 for (CmsContainer cont : containers) { 868 if (cont.isEditable() 869 && (formatters.getAllMatchingFormatters(cont.getType(), cont.getWidth()).size() > 0)) { 870 return true; 871 } 872 } 873 } catch (CmsException e) { 874 LOG.warn(e.getLocalizedMessage(), e); 875 876 } 877 return false; 878 } 879 880 /** 881 * Returns the value of the "create contents locally" flag.<p> 882 * 883 * If this flag is set, contents of types configured in a super-sitemap will be created in the sub-sitemap (if the user 884 * creates them from the sub-sitemap). 885 * 886 * @return the "create contents locally" flag 887 */ 888 public boolean isCreateContentsLocally() { 889 890 return m_data.isCreateContentsLocally(); 891 } 892 893 /** 894 * Returns the value of the "discard inherited model pages" flag.<p> 895 * 896 * If this flag is set, inherited model pages will be discarded for this sitemap.<p> 897 * 898 * @return the "discard inherited model pages" flag 899 */ 900 public boolean isDiscardInheritedModelPages() { 901 902 return m_data.isDiscardInheritedModelPages(); 903 } 904 905 /** 906 * Returns the value of the "discard inherited properties" flag.<p> 907 * 908 * If this is flag is set, inherited property definitions will be discarded for this sitemap.<p> 909 * 910 * @return the "discard inherited properties" flag.<p> 911 */ 912 public boolean isDiscardInheritedProperties() { 913 914 return m_data.isDiscardInheritedProperties(); 915 } 916 917 /** 918 * Returns the value of the "discard inherited types" flag.<p> 919 * 920 * If this flag is set, inherited resource types from a super-sitemap will be discarded for this sitemap.<p> 921 * 922 * @return the "discard inherited types" flag 923 */ 924 public boolean isDiscardInheritedTypes() { 925 926 return m_data.isDiscardInheritedTypes(); 927 } 928 929 /** 930 * Returns true if this is a module configuration instead of a normal sitemap configuration.<p> 931 * 932 * @return true if this is a module configuration 933 */ 934 public boolean isModuleConfiguration() { 935 936 return m_data.isModuleConfig(); 937 } 938 939 /** 940 * Returns true if detail pages from this sitemap should be preferred for links to contents in this sitemap.<p> 941 * 942 * @return true if detail pages from this sitemap should be preferred for links to contents in this sitemap 943 */ 944 public boolean isPreferDetailPagesForLocalContents() { 945 946 return m_data.isPreferDetailPagesForLocalContents(); 947 } 948 949 /** 950 * Fetches the parent configuration of this configuration.<p> 951 * 952 * If this configuration is a sitemap configuration with no direct parent configuration, 953 * the module configuration will be returned. If this configuration already is a module configuration, 954 * null will be returned.<p> 955 * 956 * @return the parent configuration 957 */ 958 public CmsADEConfigData parent() { 959 960 Optional<CmsADEConfigurationSequence> parentPath = m_configSequence.getParent(); 961 if (parentPath.isPresent()) { 962 CmsADEConfigDataInternal internalData = parentPath.get().getConfig(); 963 return new CmsADEConfigData(internalData, m_cache, parentPath.get()); 964 } else { 965 return null; 966 } 967 } 968 969 /** 970 * Creates the content directory for this configuration node if possible.<p> 971 * 972 * @throws CmsException if something goes wrong 973 */ 974 protected void createContentDirectory() throws CmsException { 975 976 if (!isModuleConfiguration()) { 977 String contentFolder = getContentFolderPath(); 978 if (!getCms().existsResource(contentFolder)) { 979 getCms().createResource( 980 contentFolder, 981 OpenCms.getResourceManager().getResourceType(CmsResourceTypeFolder.getStaticTypeName())); 982 } 983 } 984 } 985 986 /** 987 * Gets the CMS object used for VFS operations.<p> 988 * 989 * @return the CMS object used for VFS operations 990 */ 991 protected CmsObject getCms() { 992 993 return m_cache.getCms(); 994 } 995 996 /** 997 * Gets the CMS object used for VFS operations.<p> 998 * 999 * @return the CMS object 1000 */ 1001 protected CmsObject getCmsObject() { 1002 1003 return getCms(); 1004 } 1005 1006 /** 1007 * Helper method to converts a list of detail pages to a map from type names to lists of detail pages for each type.<p> 1008 * 1009 * @param detailPages the list of detail pages 1010 * 1011 * @return the map of detail pages 1012 */ 1013 protected Map<String, List<CmsDetailPageInfo>> getDetailPagesMap(List<CmsDetailPageInfo> detailPages) { 1014 1015 Map<String, List<CmsDetailPageInfo>> result = Maps.newHashMap(); 1016 for (CmsDetailPageInfo detailpage : detailPages) { 1017 String type = detailpage.getType(); 1018 if (!result.containsKey(type)) { 1019 result.put(type, new ArrayList<CmsDetailPageInfo>()); 1020 } 1021 result.get(type).add(detailpage); 1022 } 1023 return result; 1024 } 1025 1026 /** 1027 * Collects the folder types in a map.<p> 1028 * 1029 * @return the map of folder types 1030 * 1031 * @throws CmsException if something goes wrong 1032 */ 1033 protected Map<String, String> getFolderTypes() throws CmsException { 1034 1035 Map<String, String> result = new HashMap<String, String>(); 1036 CmsObject cms = OpenCms.initCmsObject(getCms()); 1037 if (m_data.isModuleConfig()) { 1038 Set<String> siteRoots = OpenCms.getSiteManager().getSiteRoots(); 1039 for (String siteRoot : siteRoots) { 1040 cms.getRequestContext().setSiteRoot(siteRoot); 1041 for (CmsResourceTypeConfig config : getResourceTypes()) { 1042 if (!config.isDetailPagesDisabled()) { 1043 String typeName = config.getTypeName(); 1044 if (!config.isPageRelative()) { // elements stored with container pages can not be used as detail contents 1045 String folderPath = config.getFolderPath(cms, null); 1046 result.put(CmsStringUtil.joinPaths(folderPath, "/"), typeName); 1047 } 1048 } 1049 } 1050 } 1051 } else { 1052 for (CmsResourceTypeConfig config : getResourceTypes()) { 1053 if (!config.isDetailPagesDisabled()) { 1054 String typeName = config.getTypeName(); 1055 if (!config.isPageRelative()) { // elements stored with container pages can not be used as detail contents 1056 String folderPath = config.getFolderPath(getCms(), null); 1057 result.put(CmsStringUtil.joinPaths(folderPath, "/"), typeName); 1058 } 1059 } 1060 } 1061 } 1062 return result; 1063 } 1064 1065 /** 1066 * Gets the formatter configuration for a resource type.<p> 1067 * 1068 * @param cms the current CMS context 1069 * @param resType the resource type 1070 * @param schemaFormatters the resource schema formatters 1071 * 1072 * @return the configuration of formatters for the resource type 1073 */ 1074 protected CmsFormatterConfiguration getFormatters( 1075 CmsObject cms, 1076 I_CmsResourceType resType, 1077 CmsFormatterConfiguration schemaFormatters) { 1078 1079 String typeName = resType.getTypeName(); 1080 CmsFormatterConfigurationCacheState formatterCacheState = getCachedFormatters(); 1081 List<I_CmsFormatterBean> formatters = new ArrayList<I_CmsFormatterBean>(); 1082 Set<String> types = new HashSet<String>(); 1083 types.add(typeName); 1084 for (CmsFormatterChangeSet changeSet : getFormatterChangeSets()) { 1085 if (changeSet != null) { 1086 changeSet.applyToTypes(types); 1087 } 1088 } 1089 if ((schemaFormatters != null) && types.contains(typeName)) { 1090 for (I_CmsFormatterBean formatter : schemaFormatters.getAllFormatters()) { 1091 formatters.add(formatter); 1092 } 1093 1094 } 1095 Map<CmsUUID, I_CmsFormatterBean> externalFormattersById = Maps.newHashMap(); 1096 for (I_CmsFormatterBean formatter : formatterCacheState.getFormattersForType(typeName, true)) { 1097 externalFormattersById.put(new CmsUUID(formatter.getId()), formatter); 1098 } 1099 applyAllFormatterChanges(externalFormattersById, formatterCacheState); 1100 for (I_CmsFormatterBean formatter : externalFormattersById.values()) { 1101 if (formatter.getResourceTypeNames().contains(typeName)) { 1102 formatters.add(formatter); 1103 } 1104 } 1105 return CmsFormatterConfiguration.create(cms, formatters); 1106 } 1107 1108 /** 1109 * Gets the formatters from the schema.<p> 1110 * 1111 * @param cms the current CMS context 1112 * @param res the resource for which the formatters should be retrieved 1113 * 1114 * @return the formatters from the schema 1115 */ 1116 protected CmsFormatterConfiguration getFormattersFromSchema(CmsObject cms, CmsResource res) { 1117 1118 try { 1119 return OpenCms.getResourceManager().getResourceType(res.getTypeId()).getFormattersForResource(cms, res); 1120 } catch (CmsException e) { 1121 LOG.error(e.getLocalizedMessage(), e); 1122 return CmsFormatterConfiguration.EMPTY_CONFIGURATION; 1123 } 1124 } 1125 1126 /** 1127 * Internal method for getting the function references.<p> 1128 * 1129 * @return the function references 1130 */ 1131 protected List<CmsFunctionReference> internalGetFunctionReferences() { 1132 1133 CmsADEConfigData parentData = parent(); 1134 if ((parentData == null)) { 1135 if (m_data.isModuleConfig()) { 1136 return Collections.unmodifiableList(m_data.getFunctionReferences()); 1137 } else { 1138 return Lists.newArrayList(); 1139 } 1140 } else { 1141 return parentData.internalGetFunctionReferences(); 1142 1143 } 1144 } 1145 1146 /** 1147 * Helper method for getting the list of resource types.<p> 1148 * 1149 * @param filterDisabled true if disabled types should be filtered from the result 1150 * 1151 * @return the list of resource types 1152 */ 1153 protected List<CmsResourceTypeConfig> internalGetResourceTypes(boolean filterDisabled) { 1154 1155 CmsADEConfigData parentData = parent(); 1156 List<CmsResourceTypeConfig> parentResourceTypes = null; 1157 if (parentData == null) { 1158 parentResourceTypes = Lists.newArrayList(); 1159 } else { 1160 parentResourceTypes = Lists.newArrayList(); 1161 for (CmsResourceTypeConfig typeConfig : parentData.internalGetResourceTypes(false)) { 1162 CmsResourceTypeConfig copiedType = typeConfig.copy(m_data.isDiscardInheritedTypes()); 1163 parentResourceTypes.add(copiedType); 1164 } 1165 } 1166 List<CmsResourceTypeConfig> result = combineConfigurationElements( 1167 parentResourceTypes, 1168 m_data.getOwnResourceTypes(), 1169 true); 1170 if (m_data.isCreateContentsLocally()) { 1171 for (CmsResourceTypeConfig typeConfig : result) { 1172 typeConfig.updateBasePath( 1173 CmsStringUtil.joinPaths(m_data.getBasePath(), CmsADEManager.CONTENT_FOLDER_NAME)); 1174 } 1175 } 1176 if (filterDisabled) { 1177 Iterator<CmsResourceTypeConfig> iter = result.iterator(); 1178 while (iter.hasNext()) { 1179 CmsResourceTypeConfig typeConfig = iter.next(); 1180 if (typeConfig.isDisabled()) { 1181 iter.remove(); 1182 } 1183 } 1184 } 1185 return result; 1186 } 1187 1188 /** 1189 * Merges two lists of detail pages, one from a parent configuration and one from a child configuration.<p> 1190 * 1191 * @param parentDetailPages the parent's detail pages 1192 * @param ownDetailPages the child's detail pages 1193 * 1194 * @return the merged detail pages 1195 */ 1196 protected List<CmsDetailPageInfo> mergeDetailPages( 1197 List<CmsDetailPageInfo> parentDetailPages, 1198 List<CmsDetailPageInfo> ownDetailPages) { 1199 1200 List<CmsDetailPageInfo> parentDetailPageCopies = Lists.newArrayList(); 1201 for (CmsDetailPageInfo info : parentDetailPages) { 1202 parentDetailPageCopies.add(info.copyAsInherited()); 1203 } 1204 1205 List<CmsDetailPageInfo> result = new ArrayList<CmsDetailPageInfo>(); 1206 Map<String, List<CmsDetailPageInfo>> resultDetailPageMap = Maps.newHashMap(); 1207 resultDetailPageMap.putAll(getDetailPagesMap(parentDetailPageCopies)); 1208 resultDetailPageMap.putAll(getDetailPagesMap(ownDetailPages)); 1209 result = new ArrayList<CmsDetailPageInfo>(); 1210 for (List<CmsDetailPageInfo> pages : resultDetailPageMap.values()) { 1211 result.addAll(pages); 1212 } 1213 return result; 1214 } 1215 1216 /** 1217 * Helper method to correct paths in detail page beans if the corresponding resources have been moved.<p> 1218 * 1219 * @param detailPages the original list of detail pages 1220 * 1221 * @return the corrected list of detail pages 1222 */ 1223 protected List<CmsDetailPageInfo> updateUris(List<CmsDetailPageInfo> detailPages) { 1224 1225 List<CmsDetailPageInfo> result = new ArrayList<CmsDetailPageInfo>(); 1226 for (CmsDetailPageInfo page : detailPages) { 1227 CmsUUID structureId = page.getId(); 1228 try { 1229 String rootPath = OpenCms.getADEManager().getRootPath( 1230 structureId, 1231 getCms().getRequestContext().getCurrentProject().isOnlineProject()); 1232 String iconClasses; 1233 if (page.getType().startsWith(CmsDetailPageInfo.FUNCTION_PREFIX)) { 1234 iconClasses = CmsIconUtil.getIconClasses(CmsXmlDynamicFunctionHandler.TYPE_FUNCTION, null, false); 1235 } else { 1236 iconClasses = CmsIconUtil.getIconClasses(page.getType(), null, false); 1237 } 1238 CmsDetailPageInfo correctedPage = new CmsDetailPageInfo( 1239 structureId, 1240 rootPath, 1241 page.getType(), 1242 iconClasses); 1243 result.add(page.isInherited() ? correctedPage.copyAsInherited() : correctedPage); 1244 } catch (CmsException e) { 1245 LOG.warn(e.getLocalizedMessage(), e); 1246 } 1247 } 1248 return result; 1249 } 1250}