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.CmsADEConfigData.DetailInfo; 031import org.opencms.ade.configuration.CmsElementView.ElementViewComparator; 032import org.opencms.ade.configuration.formatters.CmsFormatterConfigurationCache; 033import org.opencms.ade.configuration.formatters.CmsFormatterConfigurationCacheState; 034import org.opencms.ade.configuration.plugins.CmsTemplatePlugin; 035import org.opencms.ade.configuration.plugins.CmsTemplatePluginFinder; 036import org.opencms.ade.containerpage.inherited.CmsContainerConfigurationCache; 037import org.opencms.ade.containerpage.inherited.CmsContainerConfigurationWriter; 038import org.opencms.ade.containerpage.inherited.CmsInheritedContainerState; 039import org.opencms.ade.detailpage.CmsDetailPageConfigurationWriter; 040import org.opencms.ade.detailpage.CmsDetailPageInfo; 041import org.opencms.ade.detailpage.I_CmsDetailPageHandler; 042import org.opencms.ade.upload.CmsUploadWarningTable; 043import org.opencms.configuration.CmsSystemConfiguration; 044import org.opencms.db.I_CmsProjectDriver; 045import org.opencms.file.CmsFile; 046import org.opencms.file.CmsObject; 047import org.opencms.file.CmsProject; 048import org.opencms.file.CmsRequestContext; 049import org.opencms.file.CmsResource; 050import org.opencms.file.CmsResourceFilter; 051import org.opencms.file.CmsUser; 052import org.opencms.file.types.CmsResourceTypeFunctionConfig; 053import org.opencms.file.types.CmsResourceTypeXmlContainerPage; 054import org.opencms.file.types.CmsResourceTypeXmlContent; 055import org.opencms.file.types.I_CmsResourceType; 056import org.opencms.gwt.shared.CmsGwtConstants; 057import org.opencms.gwt.shared.CmsPermissionInfo; 058import org.opencms.gwt.shared.CmsTemplateContextInfo; 059import org.opencms.i18n.CmsEncoder; 060import org.opencms.i18n.CmsLocaleManager; 061import org.opencms.json.JSONArray; 062import org.opencms.json.JSONException; 063import org.opencms.json.JSONObject; 064import org.opencms.jsp.CmsJspNavBuilder; 065import org.opencms.jsp.CmsJspNavElement; 066import org.opencms.jsp.CmsJspTagLink; 067import org.opencms.jsp.util.CmsJspStandardContextBean; 068import org.opencms.jsp.util.CmsTemplatePluginWrapper; 069import org.opencms.loader.CmsLoaderException; 070import org.opencms.main.CmsException; 071import org.opencms.main.CmsLog; 072import org.opencms.main.OpenCms; 073import org.opencms.main.OpenCmsServlet; 074import org.opencms.monitor.CmsMemoryMonitor; 075import org.opencms.security.CmsPermissionSet; 076import org.opencms.security.CmsRole; 077import org.opencms.util.CmsRequestUtil; 078import org.opencms.util.CmsStringUtil; 079import org.opencms.util.CmsUUID; 080import org.opencms.util.CmsWaitHandle; 081import org.opencms.workplace.explorer.CmsExplorerTypeSettings; 082import org.opencms.workplace.explorer.CmsResourceUtil; 083import org.opencms.xml.CmsXmlContentDefinition; 084import org.opencms.xml.CmsXmlException; 085import org.opencms.xml.containerpage.CmsADECache; 086import org.opencms.xml.containerpage.CmsADECacheSettings; 087import org.opencms.xml.containerpage.CmsContainerElementBean; 088import org.opencms.xml.containerpage.I_CmsFormatterBean; 089import org.opencms.xml.containerpage.Messages; 090import org.opencms.xml.content.CmsXmlContent; 091import org.opencms.xml.content.CmsXmlContentFactory; 092import org.opencms.xml.content.CmsXmlContentProperty; 093import org.opencms.xml.content.CmsXmlContentProperty.Visibility; 094import org.opencms.xml.content.CmsXmlContentPropertyHelper; 095import org.opencms.xml.content.I_CmsXmlContentHandler; 096import org.opencms.xml.types.I_CmsXmlContentValue; 097 098import java.util.ArrayList; 099import java.util.Collections; 100import java.util.HashMap; 101import java.util.HashSet; 102import java.util.Iterator; 103import java.util.LinkedHashMap; 104import java.util.List; 105import java.util.Locale; 106import java.util.Map; 107import java.util.Map.Entry; 108import java.util.Set; 109import java.util.stream.Collectors; 110 111import javax.servlet.ServletRequest; 112import javax.servlet.http.HttpServletRequest; 113import javax.servlet.http.HttpServletResponse; 114 115import org.apache.commons.logging.Log; 116 117import com.google.common.collect.Lists; 118import com.google.common.collect.Maps; 119import com.google.common.collect.Multimap; 120 121/** 122 * This is the main class used to access the ADE configuration and also accomplish some other related tasks 123 * like loading/saving favorite and recent lists.<p> 124 */ 125public class CmsADEManager { 126 127 /** JSON property name constant. */ 128 protected enum FavListProp { 129 /** element property. */ 130 ELEMENT, 131 /** formatter property. */ 132 FORMATTER, 133 /** properties property. */ 134 PROPERTIES; 135 } 136 137 /** 138 * A status enum for the initialization status.<p> 139 */ 140 protected enum Status { 141 /** already initialized. */ 142 initialized, 143 /** currently initializing. */ 144 initializing, 145 /** not initialized. */ 146 notInitialized 147 } 148 149 /** The client id separator. */ 150 public static final String CLIENT_ID_SEPERATOR = "#"; 151 152 /** The configuration file name. */ 153 public static final String CONFIG_FILE_NAME = ".config"; 154 155 /** The name of the sitemap configuration file type. */ 156 public static final String CONFIG_FOLDER_TYPE = "content_folder"; 157 158 /** The path for sitemap configuration files relative from the base path. */ 159 public static final String CONFIG_SUFFIX = "/" 160 + CmsADEManager.CONTENT_FOLDER_NAME 161 + "/" 162 + CmsADEManager.CONFIG_FILE_NAME; 163 164 /** The name of the sitemap configuration file type. */ 165 public static final String CONFIG_TYPE = "sitemap_config"; 166 167 /** The content folder name. */ 168 public static final String CONTENT_FOLDER_NAME = ".content"; 169 170 /** The default detail page type name. */ 171 public static final String DEFAULT_DETAILPAGE_TYPE = CmsGwtConstants.DEFAULT_DETAILPAGE_TYPE; 172 173 /** Default favorite/recent list size constant. */ 174 public static final int DEFAULT_ELEMENT_LIST_SIZE = 10; 175 176 /** The name of the element view configuration file type. */ 177 public static final String ELEMENT_VIEW_TYPE = "elementview"; 178 179 /** The name of the module configuration file type. */ 180 public static final String MODULE_CONFIG_TYPE = "module_config"; 181 182 /** The aADE configuration module name. */ 183 public static final String MODULE_NAME_ADE_CONFIG = "org.opencms.base"; 184 185 /** Node name for the nav level link value. */ 186 public static final String N_LINK = "Link"; 187 188 /** Node name for the nav level type value. */ 189 public static final String N_TYPE = "Type"; 190 191 /** The path to the sitemap editor JSP. */ 192 public static final String PATH_SITEMAP_EDITOR_JSP = "/system/workplace/commons/sitemap.jsp"; 193 194 /** User additional info key constant. */ 195 protected static final String ADDINFO_ADE_FAVORITE_LIST = "ADE_FAVORITE_LIST"; 196 197 /** User additional info key constant. */ 198 protected static final String ADDINFO_ADE_RECENT_LIST = "ADE_RECENT_LIST"; 199 200 /** User additional info key constant. */ 201 protected static final String ADDINFO_ADE_SHOW_EDITOR_HELP = "ADE_SHOW_EDITOR_HELP"; 202 203 /** The logger instance for this class. */ 204 private static final Log LOG = CmsLog.getLog(CmsADEManager.class); 205 206 /** The cache instance. */ 207 private CmsADECache m_cache; 208 209 /** The sitemap configuration file type. */ 210 private I_CmsResourceType m_configType; 211 212 /** The detail page handler. */ 213 private I_CmsDetailPageHandler m_detailPageHandler; 214 215 /** The element view configuration file type. */ 216 private I_CmsResourceType m_elementViewType; 217 218 /** The initialization status. */ 219 private Status m_initStatus = Status.notInitialized; 220 221 /** The module configuration file type. */ 222 private I_CmsResourceType m_moduleConfigType; 223 224 /** The online cache instance. */ 225 private CmsConfigurationCache m_offlineCache; 226 227 /** The offline CMS context. */ 228 private CmsObject m_offlineCms; 229 230 /** The offline inherited container configuration cache. */ 231 private CmsContainerConfigurationCache m_offlineContainerConfigurationCache; 232 233 /** The detail id cache for the Offline project. */ 234 private CmsDetailNameCache m_offlineDetailIdCache; 235 236 /** The offline formatter bean cache. */ 237 private CmsFormatterConfigurationCache m_offlineFormatterCache; 238 239 /** The offline cache instance. */ 240 private CmsConfigurationCache m_onlineCache; 241 242 /** The online CMS context. */ 243 private CmsObject m_onlineCms; 244 245 /** The online inherited container configuration cache. */ 246 private CmsContainerConfigurationCache m_onlineContainerConfigurationCache; 247 248 /** The Online project detail id cache. */ 249 private CmsDetailNameCache m_onlineDetailIdCache; 250 251 /** The online formatter bean cache. */ 252 private CmsFormatterConfigurationCache m_onlineFormatterCache; 253 254 /** ADE parameters. */ 255 private Map<String, String> m_parameters; 256 257 /** The table of upload warnings. */ 258 private CmsUploadWarningTable m_uploadWarningTable = new CmsUploadWarningTable(); 259 260 /** 261 * Creates a new ADE manager.<p> 262 * 263 * @param adminCms a CMS context with admin privileges 264 * @param memoryMonitor the memory monitor instance 265 * @param systemConfiguration the system configuration 266 */ 267 public CmsADEManager( 268 CmsObject adminCms, 269 CmsMemoryMonitor memoryMonitor, 270 CmsSystemConfiguration systemConfiguration) { 271 272 // initialize the ade cache 273 CmsADECacheSettings cacheSettings = systemConfiguration.getAdeCacheSettings(); 274 if (cacheSettings == null) { 275 cacheSettings = new CmsADECacheSettings(); 276 } 277 m_onlineCms = adminCms; 278 m_cache = new CmsADECache(memoryMonitor, cacheSettings); 279 m_parameters = new LinkedHashMap<String, String>(systemConfiguration.getAdeParameters()); 280 m_detailPageHandler = systemConfiguration.getDetailPageHandler(); 281 // further initialization is done by the initialize() method. We don't do that in the constructor, 282 // because during the setup the configuration resource types don't exist yet. 283 } 284 285 /** 286 * Adds a wait handle for the next cache update to a formatter configuration.<p> 287 * 288 * @param online true if we want to add a wait handle to the online cache, else the offline cache 289 * @return the wait handle that has been added 290 */ 291 public CmsWaitHandle addFormatterCacheWaitHandle(boolean online) { 292 293 CmsWaitHandle handle = new CmsWaitHandle(true); // single use wait handle 294 CmsFormatterConfigurationCache cache = online ? m_onlineFormatterCache : m_offlineFormatterCache; 295 cache.addWaitHandle(handle); 296 return handle; 297 } 298 299 /** 300 * Checks if the sitemap config can be edited by the user in the given CMS context. 301 * 302 * <p>Note: Even if this returns true, there may be other reasons preventing the sitemap configuration from being edited by the user. 303 * 304 * @param cms the CMS context to check 305 * @return false if the user should not be able to edit the sitemap configuration 306 */ 307 public boolean canEditSitemapConfiguration(CmsObject cms) { 308 309 CmsRole role = getRoleForSitemapConfigEditing(); 310 if (role == null) { 311 return true; 312 } 313 return OpenCms.getRoleManager().hasRole(cms, role); 314 } 315 316 /** 317 * Finds the entry point to a sitemap.<p> 318 * 319 * @param cms the CMS context 320 * @param openPath the resource path to find the sitemap to 321 * 322 * @return the sitemap entry point 323 */ 324 public String findEntryPoint(CmsObject cms, String openPath) { 325 326 CmsADEConfigData configData = lookupConfiguration(cms, openPath); 327 String result = configData.getBasePath(); 328 if (result == null) { 329 return cms.getRequestContext().addSiteRoot("/"); 330 } 331 return result; 332 } 333 334 /** 335 * Flushes inheritance group changes so the cache is updated.<p> 336 * 337 * This is useful for test cases. 338 */ 339 public void flushInheritanceGroupChanges() { 340 341 m_offlineContainerConfigurationCache.flushUpdates(); 342 m_onlineContainerConfigurationCache.flushUpdates(); 343 } 344 345 /** 346 * Gets the complete list of beans for the currently configured detail pages.<p> 347 * 348 * @param cms the CMS context to use 349 * 350 * @return the list of detail page infos 351 */ 352 public List<CmsDetailPageInfo> getAllDetailPages(CmsObject cms) { 353 354 return getCacheState(isOnline(cms)).getAllDetailPages(); 355 } 356 357 /** 358 * Gets the containerpage cache instance.<p> 359 * 360 * @return the containerpage cache instance 361 */ 362 public CmsADECache getCache() { 363 364 return m_cache; 365 } 366 367 /** 368 * Gets the cached formatter beans.<p> 369 * 370 * @param online true if the Online project formatters should be returned, false for the Offline formatters 371 * 372 * @return the formatter configuration cache state 373 */ 374 public CmsFormatterConfigurationCacheState getCachedFormatters(boolean online) { 375 376 CmsFormatterConfigurationCache cache = online ? m_onlineFormatterCache : m_offlineFormatterCache; 377 return cache.getState(); 378 } 379 380 /** 381 * Gets the current ADE configuration cache state.<p> 382 * 383 * @param online true if you want the online state, false for the offline state 384 * 385 * @return the configuration cache state 386 */ 387 public CmsADEConfigCacheState getCacheState(boolean online) { 388 389 return (online ? m_onlineCache : m_offlineCache).getState(); 390 } 391 392 /** 393 * Gets the configuration file type.<p> 394 * 395 * @return the configuration file type 396 */ 397 public I_CmsResourceType getConfigurationType() { 398 399 return m_configType; 400 } 401 402 /** 403 * Returns the names of the bundles configured as workplace bundles in any module configuration. 404 * @return the names of the bundles configured as workplace bundles in any module configuration. 405 */ 406 public Set<String> getConfiguredWorkplaceBundles() { 407 408 CmsADEConfigData configData = internalLookupConfiguration(null, null); 409 return configData.getConfiguredWorkplaceBundles(); 410 } 411 412 /** 413 * Reads the current element bean from the request.<p> 414 * 415 * @param req the servlet request 416 * 417 * @return the element bean 418 * 419 * @throws CmsException if no current element is set 420 */ 421 public CmsContainerElementBean getCurrentElement(ServletRequest req) throws CmsException { 422 423 CmsJspStandardContextBean sCBean = CmsJspStandardContextBean.getInstance(req); 424 CmsContainerElementBean element = sCBean.getElement(); 425 if (element == null) { 426 throw new CmsException( 427 Messages.get().container( 428 Messages.ERR_READING_ELEMENT_FROM_REQUEST_1, 429 sCBean.getRequestContext().getUri())); 430 } 431 return element; 432 } 433 434 /** 435 * Gets the detail id cache for the Online or Offline projects.<p> 436 * 437 * @param online if true, gets the Online project detail id 438 * 439 * @return the detail name cache 440 */ 441 public CmsDetailNameCache getDetailIdCache(boolean online) { 442 443 return online ? m_onlineDetailIdCache : m_offlineDetailIdCache; 444 } 445 446 /** 447 * Gets the detail page information for everything.<p> 448 * 449 * @param cms the current CMS context 450 * 451 * @return the list with all the detail page information 452 */ 453 public List<DetailInfo> getDetailInfo(CmsObject cms) { 454 455 return getCacheState(isOnline(cms)).getDetailInfosForSubsites(cms); 456 } 457 458 /** 459 * Gets the detail page for a content element.<p> 460 * 461 * @param cms the CMS context 462 * @param pageRootPath the element's root path 463 * @param originPath the path in which the the detail page is being requested 464 * 465 * @return the detail page for the content element 466 */ 467 public String getDetailPage(CmsObject cms, String pageRootPath, String originPath) { 468 469 return getDetailPage(cms, pageRootPath, originPath, null); 470 } 471 472 /** 473 * Gets the detail page for a content element.<p> 474 * 475 * @param cms the CMS context 476 * @param rootPath the element's root path 477 * @param linkSource the path in which the the detail page is being requested 478 * @param targetDetailPage the target detail page to use 479 * 480 * @return the detail page for the content element 481 */ 482 public String getDetailPage(CmsObject cms, String rootPath, String linkSource, String targetDetailPage) { 483 484 return getDetailPageHandler().getDetailPage(cms, rootPath, linkSource, targetDetailPage); 485 } 486 487 /** 488 * Gets the detail page finder.<p> 489 * 490 * @return the detail page finder 491 */ 492 public I_CmsDetailPageHandler getDetailPageHandler() { 493 494 return m_detailPageHandler; 495 } 496 497 /** 498 * Returns the main detail pages for a type in all of the VFS tree.<p> 499 * 500 * @param cms the current CMS context 501 * @param type the resource type name 502 * @return a list of detail page root paths 503 */ 504 public List<String> getDetailPages(CmsObject cms, String type) { 505 506 CmsConfigurationCache cache = isOnline(cms) ? m_onlineCache : m_offlineCache; 507 return cache.getState().getDetailPages(type); 508 } 509 510 /** 511 * Gets the set of types for which detail pages are defined.<p> 512 * 513 * @param cms the current CMS context 514 * 515 * @return the set of types for which detail pages are defined 516 */ 517 public Set<String> getDetailPageTypes(CmsObject cms) { 518 519 return getCacheState(isOnline(cms)).getDetailPageTypes(); 520 } 521 522 /** 523 * Returns the element settings for a given resource.<p> 524 * 525 * @param cms the current cms context 526 * @param resource the resource 527 * 528 * @return the element settings for a given resource 529 * 530 * @throws CmsException if something goes wrong 531 */ 532 public Map<String, CmsXmlContentProperty> getElementSettings(CmsObject cms, CmsResource resource) 533 throws CmsException { 534 535 if (CmsResourceTypeXmlContent.isXmlContent(resource)) { 536 Map<String, CmsXmlContentProperty> result = new LinkedHashMap<String, CmsXmlContentProperty>(); 537 Map<String, CmsXmlContentProperty> settings = CmsXmlContentDefinition.getContentHandlerForResource( 538 cms, 539 resource).getSettings(cms, resource); 540 result.putAll(settings); 541 return CmsXmlContentPropertyHelper.copyPropertyConfiguration(result); 542 } 543 return Collections.<String, CmsXmlContentProperty> emptyMap(); 544 } 545 546 /** 547 * Returns the available element views.<p> 548 * 549 * @param cms the cms context 550 * 551 * @return the element views 552 */ 553 public Map<CmsUUID, CmsElementView> getElementViews(CmsObject cms) { 554 555 CmsConfigurationCache cache = getCache(isOnline(cms)); 556 List<CmsElementView> viewList = Lists.newArrayList(); 557 viewList.addAll(cache.getState().getElementViews().values()); 558 viewList.addAll(OpenCms.getWorkplaceManager().getExplorerTypeViews().values()); 559 Collections.sort(viewList, new ElementViewComparator()); 560 Map<CmsUUID, CmsElementView> result = Maps.newLinkedHashMap(); 561 for (CmsElementView viewValue : viewList) { 562 result.put(viewValue.getId(), viewValue); 563 } 564 return result; 565 } 566 567 /** 568 * Gets the element view configuration resource type.<p> 569 * 570 * @return the element view configuration resource type 571 */ 572 public I_CmsResourceType getElementViewType() { 573 574 return m_elementViewType; 575 } 576 577 /** 578 * Returns the favorite list, or creates it if not available.<p> 579 * 580 * @param cms the cms context 581 * 582 * @return the favorite list 583 * 584 * @throws CmsException if something goes wrong 585 */ 586 public List<CmsContainerElementBean> getFavoriteList(CmsObject cms) throws CmsException { 587 588 CmsUser user = cms.getRequestContext().getCurrentUser(); 589 Object obj = user.getAdditionalInfo(ADDINFO_ADE_FAVORITE_LIST); 590 591 List<CmsContainerElementBean> favList = new ArrayList<CmsContainerElementBean>(); 592 if (obj instanceof String) { 593 try { 594 JSONArray array = new JSONArray((String)obj); 595 for (int i = 0; i < array.length(); i++) { 596 try { 597 favList.add(elementFromJson(array.getJSONObject(i))); 598 } catch (Throwable e) { 599 // should never happen, catches wrong or no longer existing values 600 LOG.warn(e.getLocalizedMessage()); 601 } 602 } 603 } catch (Throwable e) { 604 // should never happen, catches json parsing 605 LOG.warn(e.getLocalizedMessage()); 606 } 607 } else { 608 // save to be better next time 609 saveFavoriteList(cms, favList); 610 } 611 612 return favList; 613 } 614 615 /** 616 * Returns the settings configured for the given formatter.<p> 617 * 618 * @param cms the cms context 619 * @param config the sitemap configuration 620 * @param mainFormatter the formatter 621 * @param res the element resource 622 * @param locale the content locale 623 * @param req the current request, if available 624 * 625 * @return the settings configured for the given formatter 626 */ 627 public Map<String, CmsXmlContentProperty> getFormatterSettings( 628 CmsObject cms, 629 CmsADEConfigData config, 630 I_CmsFormatterBean mainFormatter, 631 CmsResource res, 632 Locale locale, 633 ServletRequest req) { 634 635 Map<String, CmsXmlContentProperty> result = new LinkedHashMap<String, CmsXmlContentProperty>(); 636 Visibility defaultVisibility = Visibility.elementAndParentIndividual; 637 if (mainFormatter != null) { 638 for (Entry<String, CmsXmlContentProperty> entry : mainFormatter.getSettings(config).entrySet()) { 639 Visibility visibility = entry.getValue().getVisibility(defaultVisibility); 640 if (visibility.isVisibleOnElement()) { 641 result.put(entry.getKey(), entry.getValue()); 642 } 643 } 644 if (mainFormatter.hasNestedFormatterSettings()) { 645 List<I_CmsFormatterBean> nestedFormatters = getNestedFormatters(cms, config, res, locale, req); 646 if (nestedFormatters != null) { 647 for (I_CmsFormatterBean formatter : nestedFormatters) { 648 for (Entry<String, CmsXmlContentProperty> entry : formatter.getSettings(config).entrySet()) { 649 Visibility visibility = entry.getValue().getVisibility(defaultVisibility); 650 switch (visibility) { 651 case parentShared: 652 case elementAndParentShared: 653 result.put(entry.getKey(), entry.getValue()); 654 break; 655 case elementAndParentIndividual: 656 case parentIndividual: 657 String settingName = formatter.getKeyOrId() + "_" + entry.getKey(); 658 CmsXmlContentProperty settingConf = entry.getValue().withName(settingName); 659 result.put(settingName, settingConf); 660 break; 661 default: 662 break; 663 } 664 } 665 } 666 } 667 } 668 } 669 return result; 670 } 671 672 /** 673 * Returns the inheritance state for the given inheritance name and resource.<p> 674 * 675 * @param cms the current cms context 676 * @param resource the resource 677 * @param name the inheritance name 678 * 679 * @return the inheritance state 680 */ 681 public CmsInheritedContainerState getInheritedContainerState(CmsObject cms, CmsResource resource, String name) { 682 683 String rootPath = resource.getRootPath(); 684 if (!resource.isFolder()) { 685 rootPath = CmsResource.getParentFolder(rootPath); 686 } 687 CmsInheritedContainerState result = new CmsInheritedContainerState(); 688 boolean online = isOnline(cms); 689 CmsContainerConfigurationCache cache = online 690 ? m_onlineContainerConfigurationCache 691 : m_offlineContainerConfigurationCache; 692 result.addConfigurations(cache, rootPath, name); 693 return result; 694 695 } 696 697 /** 698 * Returns the inheritance state for the given inheritance name and root path.<p> 699 * 700 * @param cms the current cms context 701 * @param rootPath the root path 702 * @param name the inheritance name 703 * 704 * @return the inheritance state 705 * 706 * @throws CmsException if something goes wrong 707 */ 708 public CmsInheritedContainerState getInheritedContainerState(CmsObject cms, String rootPath, String name) 709 throws CmsException { 710 711 String oldSiteRoot = cms.getRequestContext().getSiteRoot(); 712 try { 713 cms.getRequestContext().setSiteRoot(""); 714 CmsResource resource = cms.readResource(rootPath); 715 return getInheritedContainerState(cms, resource, name); 716 } finally { 717 cms.getRequestContext().setSiteRoot(oldSiteRoot); 718 } 719 } 720 721 /** 722 * Gets the maximum sitemap depth.<p> 723 * 724 * @return the maximum sitemap depth 725 */ 726 public int getMaxSitemapDepth() { 727 728 return 20; 729 } 730 731 /** 732 * Gets the module configuration resource type.<p> 733 * 734 * @return the module configuration resource type 735 */ 736 public I_CmsResourceType getModuleConfigurationType() { 737 738 return m_moduleConfigType; 739 } 740 741 /** 742 * Returns the nested formatters of the given resource.<p> 743 * 744 * @param cms the cms context 745 * @param config the sitemap configuration 746 * @param res the resource 747 * @param locale the content locale 748 * @param req the request, if available 749 * 750 * @return the nested formatters 751 */ 752 public List<I_CmsFormatterBean> getNestedFormatters( 753 CmsObject cms, 754 CmsADEConfigData config, 755 CmsResource res, 756 Locale locale, 757 ServletRequest req) { 758 759 List<I_CmsFormatterBean> result = null; 760 if (CmsResourceTypeXmlContent.isXmlContent(res)) { 761 CmsResourceTypeXmlContent type = (CmsResourceTypeXmlContent)OpenCms.getResourceManager().getResourceType( 762 res); 763 String schema = type.getSchema(); 764 try { 765 CmsXmlContentDefinition contentDefinition = CmsXmlContentDefinition.unmarshal(cms, schema); 766 // get the content handler for the resource type to create 767 I_CmsXmlContentHandler handler = contentDefinition.getContentHandler(); 768 if (handler.hasNestedFormatters()) { 769 result = new ArrayList<I_CmsFormatterBean>(); 770 for (String formatterId : handler.getNestedFormatters(cms, res, locale, req)) { 771 I_CmsFormatterBean formatter = config.findFormatter(formatterId); 772 if (formatter != null) { 773 result.add(formatter); 774 } 775 } 776 } 777 } catch (CmsXmlException e) { 778 LOG.error(e.getMessage(), e); 779 } 780 } 781 return result; 782 } 783 784 /** 785 * Gets ADE parameters.<p> 786 * 787 * @param cms the current CMS context 788 * @return the ADE parameters for the current user 789 */ 790 public Map<String, String> getParameters(CmsObject cms) { 791 792 Map<String, String> result = new LinkedHashMap<String, String>(m_parameters); 793 if (cms != null) { 794 String userParamsStr = (String)(cms.getRequestContext().getCurrentUser().getAdditionalInfo().get( 795 "ADE_PARAMS")); 796 if (userParamsStr != null) { 797 Map<String, String> userParams = CmsStringUtil.splitAsMap(userParamsStr, "|", ":"); 798 result.putAll(userParams); 799 } 800 } 801 return result; 802 } 803 804 /** 805 * Gets the content element type for the given path's parent folder. 806 * 807 * @param online true if we want to use the Online project's configuration 808 * @param rootPath the root path of a content 809 * 810 * @return the parent folder type name, or null if none is defined 811 */ 812 public String getParentFolderType(boolean online, String rootPath) { 813 814 return getCacheState(online).getParentFolderType(rootPath); 815 816 } 817 818 /** 819 * Returns the permission info for the given resource.<p> 820 * 821 * @param cms the cms context 822 * @param resource the resource 823 * @param contextPath the context path 824 * 825 * @return the permission info 826 * 827 * @throws CmsException if checking the permissions fails 828 */ 829 public CmsPermissionInfo getPermissionInfo(CmsObject cms, CmsResource resource, String contextPath) 830 throws CmsException { 831 832 boolean hasView = cms.hasPermissions( 833 resource, 834 CmsPermissionSet.ACCESS_VIEW, 835 false, 836 CmsResourceFilter.ALL.addRequireVisible()); 837 boolean hasWrite = false; 838 if (hasView) { 839 try { 840 I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(resource.getTypeId()); 841 CmsExplorerTypeSettings settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting( 842 type.getTypeName()); 843 hasView = (settings == null) 844 || settings.getAccess().getPermissions(cms, resource).requiresViewPermission(); 845 if (hasView 846 && CmsResourceTypeXmlContent.isXmlContent(resource) 847 && !CmsResourceTypeXmlContainerPage.isContainerPage(resource)) { 848 if (contextPath == null) { 849 contextPath = resource.getRootPath(); 850 } 851 CmsResourceTypeConfig localConfigData = lookupConfigurationWithCache( 852 cms, 853 contextPath).getResourceType(type.getTypeName()); 854 if (localConfigData != null) { 855 Map<CmsUUID, CmsElementView> elementViews = getElementViews(cms); 856 hasView = elementViews.containsKey(localConfigData.getElementView()) 857 && elementViews.get(localConfigData.getElementView()).hasPermission(cms, resource); 858 } 859 } 860 // the user may only have write permissions if he is allowed to view the resource 861 hasWrite = hasView 862 && cms.hasPermissions( 863 resource, 864 CmsPermissionSet.ACCESS_WRITE, 865 false, 866 CmsResourceFilter.IGNORE_EXPIRATION) 867 && ((settings == null) 868 || settings.getAccess().getPermissions(cms, resource).requiresWritePermission()); 869 } catch (CmsLoaderException e) { 870 LOG.warn(e.getLocalizedMessage(), e); 871 hasWrite = false; 872 } 873 } 874 875 if (hasWrite && isEditorRestricted(cms, resource)) { 876 hasWrite = false; 877 } 878 879 String noEdit = new CmsResourceUtil(cms, resource).getNoEditReason( 880 OpenCms.getWorkplaceManager().getWorkplaceLocale(cms), 881 true); 882 883 boolean isFunction = false; 884 for (String type : new String[] {"function", CmsResourceTypeFunctionConfig.TYPE_NAME}) { 885 if (OpenCms.getResourceManager().matchResourceType(type, resource.getTypeId())) { 886 isFunction = true; 887 break; 888 } 889 } 890 if (isFunction) { 891 Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms); 892 noEdit = Messages.get().getBundle(locale).key(Messages.GUI_CANT_EDIT_FUNCTIONS_0); 893 } 894 895 return new CmsPermissionInfo(hasView, hasWrite, noEdit); 896 } 897 898 /** 899 * Gets a map of plugin wrappers for the given site path. 900 * 901 * <p>This *only* includes plugins defined in site plugins active on the given path, not those referenced in formatters. 902 * 903 * @param cms the CMS context 904 * @param path the path for which to get the plugins 905 * 906 * @return the map of plugin wrappers, with the plugin groups as keys 907 */ 908 public Map<String, List<CmsTemplatePluginWrapper>> getPluginsForPath(CmsObject cms, String path) { 909 910 CmsADEConfigData config = lookupConfigurationWithCache(cms, cms.getRequestContext().addSiteRoot(path)); 911 912 Multimap<String, CmsTemplatePlugin> plugins = CmsTemplatePluginFinder.getActiveTemplatePluginsFromSitePlugins( 913 config); 914 Map<String, List<CmsTemplatePluginWrapper>> result = new HashMap<>(); 915 for (String key : plugins.keySet()) { 916 List<CmsTemplatePluginWrapper> wrappers = plugins.get(key).stream().map( 917 plugin -> new CmsTemplatePluginWrapper(cms, plugin)).collect(Collectors.toList()); 918 result.put(key, Collections.unmodifiableList(wrappers)); 919 } 920 return Collections.unmodifiableMap(result); 921 922 } 923 924 /** 925 * Gets the raw configured detail page information, with no existence checks or path correction. 926 * 927 * @param cms the CMS context 928 * @return the list of raw detail page info beans 929 */ 930 public List<CmsDetailPageInfo> getRawDetailPages(CmsObject cms) { 931 932 return getCache(cms.getRequestContext().getCurrentProject().isOnlineProject()).getRawDetailPages(); 933 } 934 935 /** 936 * Returns the favorite list, or creates it if not available.<p> 937 * 938 * @param cms the cms context 939 * 940 * @return the favorite list 941 * 942 * @throws CmsException if something goes wrong 943 */ 944 public List<CmsContainerElementBean> getRecentList(CmsObject cms) throws CmsException { 945 946 CmsUser user = cms.getRequestContext().getCurrentUser(); 947 Object obj = user.getAdditionalInfo(ADDINFO_ADE_RECENT_LIST); 948 949 List<CmsContainerElementBean> recentList = new ArrayList<CmsContainerElementBean>(); 950 if (obj instanceof String) { 951 try { 952 JSONArray array = new JSONArray((String)obj); 953 for (int i = 0; i < array.length(); i++) { 954 try { 955 recentList.add(elementFromJson(array.getJSONObject(i))); 956 } catch (Throwable e) { 957 // should never happen, catches wrong or no longer existing values 958 LOG.warn(e.getLocalizedMessage()); 959 } 960 } 961 } catch (Throwable e) { 962 // should never happen, catches json parsing 963 LOG.warn(e.getLocalizedMessage()); 964 } 965 } else { 966 // save to be better next time 967 saveRecentList(cms, recentList); 968 } 969 970 return recentList; 971 } 972 973 /** 974 * Gets the sitemap configuration resource type.<p> 975 * 976 * @return the resource type for sitemap configurations 977 */ 978 public I_CmsResourceType getSitemapConfigurationType() { 979 980 return m_configType; 981 } 982 983 /** 984 * Returns all sub sites below the given path.<p> 985 * 986 * @param cms the cms context 987 * @param subSiteRoot the sub site root path 988 * 989 * @return the sub site root paths 990 */ 991 public List<String> getSubSitePaths(CmsObject cms, String subSiteRoot) { 992 993 List<String> result = new ArrayList<String>(); 994 String normalizedRootPath = CmsStringUtil.joinPaths("/", subSiteRoot, "/"); 995 CmsADEConfigCacheState state = getCacheState(isOnline(cms)); 996 Set<String> siteConfigurationPaths = state.getSiteConfigurationPaths(); 997 for (String path : siteConfigurationPaths) { 998 if ((path.length() > normalizedRootPath.length()) && path.startsWith(normalizedRootPath)) { 999 result.add(path); 1000 } 1001 } 1002 return result; 1003 } 1004 1005 /** 1006 * Tries to get the subsite root for a given resource root path.<p> 1007 * 1008 * @param cms the current CMS context 1009 * @param rootPath the root path for which the subsite root should be found 1010 * 1011 * @return the subsite root 1012 */ 1013 public String getSubSiteRoot(CmsObject cms, String rootPath) { 1014 1015 CmsADEConfigData configData = lookupConfiguration(cms, rootPath); 1016 String basePath = configData.getBasePath(); 1017 String siteRoot = OpenCms.getSiteManager().getSiteRoot(rootPath); 1018 if (siteRoot == null) { 1019 siteRoot = ""; 1020 } 1021 if ((basePath == null) || !basePath.startsWith(siteRoot)) { 1022 // the subsite root should always be below the site root 1023 return siteRoot; 1024 } else { 1025 return basePath; 1026 } 1027 } 1028 1029 /** 1030 * Gets the subsites to be displayed in the site selector. 1031 * 1032 * @param online true if we want the subsites for the Online project 1033 * 1034 * @return the subsites to be displayed in the site selector 1035 */ 1036 public List<String> getSubsitesForSiteSelector(boolean online) { 1037 1038 return getCacheState(online).getSubsitesForSiteSelector(); 1039 1040 } 1041 1042 /** 1043 * Gets the table of upload warnings. 1044 * 1045 * @return the table of upload warnings 1046 */ 1047 public CmsUploadWarningTable getUploadWarningTable() { 1048 1049 return m_uploadWarningTable; 1050 } 1051 1052 /** 1053 * Processes a HTML redirect content.<p> 1054 * 1055 * This needs to be in the ADE manager because the user for whom the HTML redirect is being loaded 1056 * does not necessarily have read permissions for the redirect target, so we read the redirect target 1057 * with admin privileges.<p> 1058 * 1059 * @param userCms the CMS context of the current user 1060 * @param request the servlet request 1061 * @param response the servlet response 1062 * @param htmlRedirect the path of the HTML redirect resource 1063 * 1064 * @throws CmsException if something goes wrong 1065 */ 1066 public void handleHtmlRedirect( 1067 CmsObject userCms, 1068 HttpServletRequest request, 1069 HttpServletResponse response, 1070 String htmlRedirect) 1071 throws CmsException { 1072 1073 CmsObject cms = OpenCms.initCmsObject(m_offlineCms); 1074 CmsRequestContext userContext = userCms.getRequestContext(); 1075 CmsRequestContext currentContext = cms.getRequestContext(); 1076 currentContext.setCurrentProject(userContext.getCurrentProject()); 1077 currentContext.setSiteRoot(userContext.getSiteRoot()); 1078 currentContext.setLocale(userContext.getLocale()); 1079 currentContext.setUri(userContext.getUri()); 1080 1081 CmsFile file = cms.readFile(htmlRedirect); 1082 CmsXmlContent content = CmsXmlContentFactory.unmarshal(cms, file); 1083 1084 // find out the locale to use for reading values from the redirect 1085 List<Locale> candidates = new ArrayList<Locale>(); 1086 candidates.add(currentContext.getLocale()); 1087 candidates.add(CmsLocaleManager.getDefaultLocale()); 1088 candidates.add(Locale.ENGLISH); 1089 candidates.addAll(content.getLocales()); 1090 Locale contentLocale = currentContext.getLocale(); 1091 for (Locale candidateLocale : candidates) { 1092 if (content.hasLocale(candidateLocale)) { 1093 contentLocale = candidateLocale; 1094 break; 1095 } 1096 } 1097 1098 String typeValue = content.getValue(N_TYPE, contentLocale).getStringValue(cms); 1099 String lnkUri = ""; 1100 Integer errorCode; 1101 if ("sublevel".equals(typeValue)) { 1102 // use the nav builder to get the first sub level entry 1103 CmsJspNavBuilder navBuilder = new CmsJspNavBuilder(cms); 1104 if (navBuilder.getNavigationForFolder().size() > 0) { 1105 CmsJspNavElement target = navBuilder.getNavigationForFolder().get(0); 1106 lnkUri = CmsJspTagLink.linkTagAction(target.getResourceName(), request); 1107 errorCode = Integer.valueOf(HttpServletResponse.SC_MOVED_TEMPORARILY); 1108 } else { 1109 // send error 404 if no sub entry available 1110 errorCode = Integer.valueOf(HttpServletResponse.SC_NOT_FOUND); 1111 } 1112 } else { 1113 I_CmsXmlContentValue contentValue = content.getValue(N_LINK, contentLocale); 1114 if (contentValue != null) { 1115 String linkValue = contentValue.getStringValue(cms); 1116 lnkUri = OpenCms.getLinkManager().substituteLinkForUnknownTarget(cms, linkValue); 1117 try { 1118 errorCode = Integer.valueOf(typeValue); 1119 } catch (NumberFormatException e) { 1120 LOG.error(e.getMessage(), e); 1121 // fall back to default 1122 errorCode = Integer.valueOf(307); 1123 } 1124 } else { 1125 // send error 404 if no link value is set 1126 errorCode = Integer.valueOf(HttpServletResponse.SC_NOT_FOUND); 1127 } 1128 } 1129 if (!currentContext.getCurrentProject().isOnlineProject()) { 1130 // permanent redirects are confusing and not useful in the Offline project because they are stored 1131 // by the browser based on the host name, not the site the user is working in. 1132 if (errorCode.intValue() == HttpServletResponse.SC_MOVED_PERMANENTLY) { 1133 errorCode = Integer.valueOf(HttpServletResponse.SC_MOVED_TEMPORARILY); 1134 } 1135 } 1136 request.setAttribute(CmsRequestUtil.ATTRIBUTE_ERRORCODE, errorCode); 1137 response.setHeader("Location", CmsEncoder.convertHostToPunycode(lnkUri)); 1138 response.setHeader("Connection", "close"); 1139 response.setStatus(errorCode.intValue()); 1140 } 1141 1142 /** 1143 * Initializes the configuration by reading all configuration files and caching their data.<p> 1144 */ 1145 public synchronized void initialize() { 1146 1147 // no need to try initialization in case the 'org.opencms.base' is not present and the contained resource types missing 1148 if ((m_initStatus == Status.notInitialized) && OpenCms.getModuleManager().hasModule(MODULE_NAME_ADE_CONFIG)) { 1149 try { 1150 CmsLog.INIT.info(". Initializing the ADE configuration, this may take a while..."); 1151 m_initStatus = Status.initializing; 1152 m_configType = OpenCms.getResourceManager().getResourceType(CONFIG_TYPE); 1153 m_moduleConfigType = OpenCms.getResourceManager().getResourceType(MODULE_CONFIG_TYPE); 1154 m_elementViewType = OpenCms.getResourceManager().getResourceType(ELEMENT_VIEW_TYPE); 1155 CmsProject temp = getTempfileProject(m_onlineCms); 1156 m_offlineCms = OpenCms.initCmsObject(m_onlineCms); 1157 m_offlineCms.getRequestContext().setCurrentProject(temp); 1158 m_onlineCache = new CmsConfigurationCache( 1159 m_onlineCms, 1160 m_configType, 1161 m_moduleConfigType, 1162 m_elementViewType); 1163 m_offlineCache = new CmsConfigurationCache( 1164 m_offlineCms, 1165 m_configType, 1166 m_moduleConfigType, 1167 m_elementViewType); 1168 CmsLog.INIT.info(". Reading online configuration..."); 1169 m_onlineCache.initialize(); 1170 CmsLog.INIT.info(". Reading offline configuration..."); 1171 m_offlineCache.initialize(); 1172 m_onlineContainerConfigurationCache = new CmsContainerConfigurationCache( 1173 m_onlineCms, 1174 "online inheritance groups"); 1175 m_offlineContainerConfigurationCache = new CmsContainerConfigurationCache( 1176 m_offlineCms, 1177 "offline inheritance groups"); 1178 CmsLog.INIT.info(". Reading online inherited container configurations..."); 1179 m_onlineContainerConfigurationCache.initialize(); 1180 CmsLog.INIT.info(". Reading offline inherited container configurations..."); 1181 m_offlineContainerConfigurationCache.initialize(); 1182 m_offlineFormatterCache = new CmsFormatterConfigurationCache(m_offlineCms, "offline formatters"); 1183 m_onlineFormatterCache = new CmsFormatterConfigurationCache(m_onlineCms, "online formatters"); 1184 CmsLog.INIT.info(". Reading online formatter configurations..."); 1185 m_onlineFormatterCache.initialize(); 1186 CmsLog.INIT.info(". Reading offline formatter configurations..."); 1187 m_offlineFormatterCache.initialize(); 1188 1189 m_offlineDetailIdCache = new CmsDetailNameCache(m_offlineCms); 1190 m_onlineDetailIdCache = new CmsDetailNameCache(m_onlineCms); 1191 CmsLog.INIT.info(". Initializing online detail name cache..."); 1192 m_onlineDetailIdCache.initialize(); 1193 CmsLog.INIT.info(". Initializing offline detail name cache..."); 1194 m_offlineDetailIdCache.initialize(); 1195 1196 CmsGlobalConfigurationCacheEventHandler handler = new CmsGlobalConfigurationCacheEventHandler( 1197 m_onlineCms); 1198 handler.addCache(m_offlineCache, m_onlineCache, "ADE configuration cache"); 1199 handler.addCache( 1200 m_offlineContainerConfigurationCache, 1201 m_onlineContainerConfigurationCache, 1202 "Inherited container cache"); 1203 handler.addCache(m_offlineFormatterCache, m_onlineFormatterCache, "formatter configuration cache"); 1204 handler.addCache(m_offlineDetailIdCache, m_onlineDetailIdCache, "Detail ID cache"); 1205 OpenCms.getEventManager().addCmsEventListener(handler); 1206 CmsLog.INIT.info(". Done initializing the ADE configuration."); 1207 m_initStatus = Status.initialized; 1208 } catch (CmsException e) { 1209 m_initStatus = Status.notInitialized; 1210 LOG.error(e.getLocalizedMessage(), e); 1211 } 1212 m_detailPageHandler.initialize(m_offlineCms, m_onlineCms); 1213 } 1214 } 1215 1216 /** 1217 * Checks whether the given resource is configured as a detail page.<p> 1218 * 1219 * @param cms the current CMS context 1220 * @param resource the resource which should be tested 1221 * 1222 * @return true if the resource is configured as a detail page 1223 */ 1224 public boolean isDetailPage(CmsObject cms, CmsResource resource) { 1225 1226 return getCache(isOnline(cms)).isDetailPage(cms, resource); 1227 } 1228 1229 /** 1230 * Checks if the user should be prevented from editing a file. 1231 * 1232 * <p>This is not a permission check, but an additional mechanism to prevent users from editing configuration files even if they technically need or have write permissions for these files. 1233 * 1234 * @param cms the CMS context 1235 * @param res the resource to check 1236 * @return true if the user should be prevented from editing the file 1237 */ 1238 public boolean isEditorRestricted(CmsObject cms, CmsResource res) { 1239 1240 if (OpenCms.getResourceManager().matchResourceType(CONFIG_TYPE, res.getTypeId())) { 1241 CmsRole role = getRoleForSitemapConfigEditing(); 1242 if ((role != null) && !OpenCms.getRoleManager().hasRoleForResource(cms, role, res)) { 1243 return true; 1244 } 1245 } 1246 return false; 1247 } 1248 1249 /** 1250 * Checks whether the ADE manager is initialized (this should usually be the case except during the setup).<p> 1251 * 1252 * @return true if the ADE manager is initialized 1253 */ 1254 public boolean isInitialized() { 1255 1256 return m_initStatus == Status.initialized; 1257 } 1258 1259 /** 1260 * Returns the show editor help flag.<p> 1261 * 1262 * @param cms the cms context 1263 * 1264 * @return the show editor help flag 1265 */ 1266 public boolean isShowEditorHelp(CmsObject cms) { 1267 1268 CmsUser user = cms.getRequestContext().getCurrentUser(); 1269 String showHelp = (String)user.getAdditionalInfo(ADDINFO_ADE_SHOW_EDITOR_HELP); 1270 return CmsStringUtil.isEmptyOrWhitespaceOnly(showHelp) || Boolean.parseBoolean(showHelp); 1271 } 1272 1273 /** 1274 * Looks up the configuration data for a given sitemap path.<p> 1275 * 1276 * @param cms the current CMS context 1277 * @param rootPath the root path for which the configuration data should be looked up 1278 * 1279 * @return the configuration data 1280 */ 1281 public CmsADEConfigData lookupConfiguration(CmsObject cms, String rootPath) { 1282 1283 CmsADEConfigData configData = internalLookupConfiguration(cms, rootPath); 1284 return configData; 1285 } 1286 1287 /** 1288 * Looks up the configuration data for a given sitemap path, but uses a thread-local cache for the current request for efficiency. 1289 * 1290 * @param cms the current CMS context 1291 * @param rootPath the root path for which the configuration data should be looked up 1292 * 1293 * @return the configuration data 1294 */ 1295 public CmsADEConfigData lookupConfigurationWithCache(CmsObject cms, String rootPath) { 1296 1297 boolean online = (cms == null) || cms.getRequestContext().getCurrentProject().isOnlineProject(); 1298 String cacheKey = "" + online + ":" + rootPath; 1299 OpenCmsServlet.RequestCache context = OpenCmsServlet.getRequestCache(); 1300 CmsADEConfigData result = null; 1301 if (context != null) { 1302 result = context.getCachedConfig(cacheKey); 1303 } 1304 if (result == null) { 1305 result = internalLookupConfiguration(cms, rootPath); 1306 if (context != null) { 1307 context.setCachedConfig(cacheKey, result); 1308 } 1309 } 1310 return result; 1311 } 1312 1313 /** 1314 * Reloads the configuration.<p> 1315 * 1316 * Normally you shouldn't call this directly since the event handlers take care of updating the configuration. 1317 */ 1318 public void refresh() { 1319 1320 m_onlineCache.initialize(); 1321 m_offlineCache.initialize(); 1322 } 1323 1324 /** 1325 * Saves a list of detail pages.<p> 1326 * @param cms the cms context 1327 * @param rootPath the root path 1328 * @param detailPages the detail pages 1329 * @param newId the id to use for new detail pages without an id 1330 * @return true if the detail pages could be successfully saved 1331 * 1332 * @throws CmsException if something goes wrong 1333 */ 1334 public boolean saveDetailPages(CmsObject cms, String rootPath, List<CmsDetailPageInfo> detailPages, CmsUUID newId) 1335 throws CmsException { 1336 1337 CmsADEConfigData configData = lookupConfiguration(cms, rootPath); 1338 CmsDetailPageConfigurationWriter configWriter; 1339 String originalSiteRoot = cms.getRequestContext().getSiteRoot(); 1340 try { 1341 cms.getRequestContext().setSiteRoot(""); 1342 if (configData.isModuleConfiguration()) { 1343 return false; 1344 } 1345 CmsResource configFile = configData.getResource(); 1346 configWriter = new CmsDetailPageConfigurationWriter(cms, configFile); 1347 configWriter.updateAndSave(detailPages, newId); 1348 return true; 1349 } finally { 1350 cms.getRequestContext().setSiteRoot(originalSiteRoot); 1351 } 1352 } 1353 1354 /** 1355 * Saves the favorite list, user based.<p> 1356 * 1357 * @param cms the cms context 1358 * @param favoriteList the element list 1359 * 1360 * @throws CmsException if something goes wrong 1361 */ 1362 public void saveFavoriteList(CmsObject cms, List<CmsContainerElementBean> favoriteList) throws CmsException { 1363 1364 saveElementList(cms, favoriteList, ADDINFO_ADE_FAVORITE_LIST); 1365 } 1366 1367 /** 1368 * Saves the inheritance container information.<p> 1369 * 1370 * @param cms the current cms context 1371 * @param pageResource the resource or parent folder 1372 * @param name the inheritance name 1373 * @param newOrder if the element have been reordered 1374 * @param elements the elements 1375 * 1376 * @throws CmsException if something goes wrong 1377 */ 1378 public void saveInheritedContainer( 1379 CmsObject cms, 1380 CmsResource pageResource, 1381 String name, 1382 boolean newOrder, 1383 List<CmsContainerElementBean> elements) 1384 throws CmsException { 1385 1386 CmsContainerConfigurationWriter writer = new CmsContainerConfigurationWriter(); 1387 writer.save(cms, name, newOrder, pageResource, elements); 1388 1389 // Inheritance groups are usually reloaded directly after saving them, 1390 // so the cache needs to be up to date after this method is called 1391 m_offlineContainerConfigurationCache.flushUpdates(); 1392 } 1393 1394 /** 1395 * Saves the inheritance container information.<p> 1396 * 1397 * @param cms the current cms context 1398 * @param sitePath the site path of the resource or parent folder 1399 * @param name the inheritance name 1400 * @param newOrder if the element have been reordered 1401 * @param elements the elements 1402 * 1403 * @throws CmsException if something goes wrong 1404 */ 1405 public void saveInheritedContainer( 1406 CmsObject cms, 1407 String sitePath, 1408 String name, 1409 boolean newOrder, 1410 List<CmsContainerElementBean> elements) 1411 throws CmsException { 1412 1413 saveInheritedContainer(cms, cms.readResource(sitePath), name, newOrder, elements); 1414 } 1415 1416 /** 1417 * Saves the favorite list, user based.<p> 1418 * 1419 * @param cms the cms context 1420 * @param recentList the element list 1421 * 1422 * @throws CmsException if something goes wrong 1423 */ 1424 public void saveRecentList(CmsObject cms, List<CmsContainerElementBean> recentList) throws CmsException { 1425 1426 saveElementList(cms, recentList, ADDINFO_ADE_RECENT_LIST); 1427 } 1428 1429 /** 1430 * Sets the show editor help flag.<p> 1431 * 1432 * @param cms the cms context 1433 * @param showHelp the show help flag 1434 * @throws CmsException if writing the user info fails 1435 */ 1436 public void setShowEditorHelp(CmsObject cms, boolean showHelp) throws CmsException { 1437 1438 CmsUser user = cms.getRequestContext().getCurrentUser(); 1439 user.setAdditionalInfo(ADDINFO_ADE_SHOW_EDITOR_HELP, String.valueOf(showHelp)); 1440 cms.writeUser(user); 1441 } 1442 1443 /** 1444 * The method which is called when the OpenCms instance is shut down.<p> 1445 */ 1446 public void shutdown() { 1447 1448 // do nothing 1449 } 1450 1451 /** 1452 * Waits until the next time the cache is updated.<p> 1453 * 1454 * @param online true if we want to wait for the online cache, false for the offline cache 1455 */ 1456 public void waitForCacheUpdate(boolean online) { 1457 1458 getCache(online).getWaitHandleForUpdateTask().enter(2 * CmsConfigurationCache.TASK_DELAY_MILLIS); 1459 } 1460 1461 /** 1462 * Waits until the formatter cache has finished updating itself.<p> 1463 * 1464 * This method is only intended for use in test cases. 1465 * 1466 * @param online true if we should wait for the online formatter cache,false for the offline cache 1467 */ 1468 public void waitForFormatterCache(boolean online) { 1469 1470 CmsFormatterConfigurationCache cache = online ? m_onlineFormatterCache : m_offlineFormatterCache; 1471 cache.waitForUpdate(); 1472 } 1473 1474 /** 1475 * Creates an element from its serialized data.<p> 1476 * 1477 * @param data the serialized data 1478 * 1479 * @return the restored element bean 1480 * 1481 * @throws JSONException if the serialized data got corrupted 1482 */ 1483 protected CmsContainerElementBean elementFromJson(JSONObject data) throws JSONException { 1484 1485 CmsUUID element = new CmsUUID(data.getString(FavListProp.ELEMENT.name().toLowerCase())); 1486 CmsUUID formatter = null; 1487 if (data.has(FavListProp.FORMATTER.name().toLowerCase())) { 1488 formatter = new CmsUUID(data.getString(FavListProp.FORMATTER.name().toLowerCase())); 1489 } 1490 Map<String, String> properties = new HashMap<String, String>(); 1491 1492 JSONObject props = data.getJSONObject(FavListProp.PROPERTIES.name().toLowerCase()); 1493 Iterator<String> keys = props.keys(); 1494 while (keys.hasNext()) { 1495 String key = keys.next(); 1496 properties.put(key, props.getString(key)); 1497 } 1498 1499 return new CmsContainerElementBean(element, formatter, properties, false); 1500 } 1501 1502 /** 1503 * Converts the given element to JSON.<p> 1504 * 1505 * @param element the element to convert 1506 * @param excludeSettings the keys of settings which should not be written to the JSON 1507 * 1508 * @return the JSON representation 1509 */ 1510 protected JSONObject elementToJson(CmsContainerElementBean element, Set<String> excludeSettings) { 1511 1512 JSONObject data = null; 1513 try { 1514 data = new JSONObject(); 1515 data.put(FavListProp.ELEMENT.name().toLowerCase(), element.getId().toString()); 1516 if (element.getFormatterId() != null) { 1517 data.put(FavListProp.FORMATTER.name().toLowerCase(), element.getFormatterId().toString()); 1518 } 1519 JSONObject properties = new JSONObject(); 1520 for (Map.Entry<String, String> entry : element.getIndividualSettings().entrySet()) { 1521 String settingKey = entry.getKey(); 1522 if (!excludeSettings.contains(settingKey)) { 1523 properties.put(entry.getKey(), entry.getValue()); 1524 } 1525 } 1526 data.put(FavListProp.PROPERTIES.name().toLowerCase(), properties); 1527 } catch (JSONException e) { 1528 // should never happen 1529 if (!LOG.isDebugEnabled()) { 1530 LOG.warn(e.getLocalizedMessage()); 1531 } 1532 LOG.debug(e.getLocalizedMessage(), e); 1533 return null; 1534 } 1535 return data; 1536 } 1537 1538 /** 1539 * Gets the configuration cache instance.<p> 1540 * 1541 * @param online true if you want the online cache, false for the offline cache 1542 * 1543 * @return the ADE configuration cache instance 1544 */ 1545 protected CmsConfigurationCache getCache(boolean online) { 1546 1547 return online ? m_onlineCache : m_offlineCache; 1548 } 1549 1550 /** 1551 * Gets the offline cache.<p> 1552 * 1553 * @return the offline configuration cache 1554 */ 1555 protected CmsConfigurationCache getOfflineCache() { 1556 1557 return m_offlineCache; 1558 } 1559 1560 /** 1561 * Gets the online cache.<p> 1562 * 1563 * @return the online configuration cache 1564 */ 1565 protected CmsConfigurationCache getOnlineCache() { 1566 1567 return m_onlineCache; 1568 } 1569 1570 /** 1571 * Gets the role necessary to edit sitemap configuration files. 1572 * 1573 * @return the role needed for editing sitemap configurations 1574 */ 1575 protected CmsRole getRoleForSitemapConfigEditing() { 1576 1577 String roleName = OpenCms.getWorkplaceManager().getSitemapConfigEditRole(); 1578 if (roleName == null) { 1579 return null; 1580 } else { 1581 if (roleName.indexOf("/") == -1) { 1582 return CmsRole.valueOfRoleName(roleName).forOrgUnit(null); 1583 } else { 1584 return CmsRole.valueOfRoleName(roleName); 1585 } 1586 } 1587 } 1588 1589 /** 1590 * Gets the root path for a given resource structure id.<p> 1591 * 1592 * @param structureId the structure id 1593 * @param online if true, the resource will be looked up in the online project ,else in the offline project 1594 * 1595 * @return the root path for the given structure id 1596 * 1597 * @throws CmsException if something goes wrong 1598 */ 1599 protected String getRootPath(CmsUUID structureId, boolean online) throws CmsException { 1600 1601 CmsConfigurationCache cache = online ? m_onlineCache : m_offlineCache; 1602 return cache.getPathForStructureId(structureId); 1603 } 1604 1605 /** 1606 * Gets a tempfile project, creating one if it doesn't exist already.<p> 1607 * 1608 * @param cms the CMS context to use 1609 * @return the tempfile project 1610 * 1611 * @throws CmsException if something goes wrong 1612 */ 1613 protected CmsProject getTempfileProject(CmsObject cms) throws CmsException { 1614 1615 try { 1616 return cms.readProject(I_CmsProjectDriver.TEMP_FILE_PROJECT_NAME); 1617 } catch (CmsException e) { 1618 return cms.createTempfileProject(); 1619 } 1620 } 1621 1622 /** 1623 * Internal configuration lookup method.<p> 1624 * 1625 * @param cms the cms context 1626 * @param rootPath the root path for which to look up the configuration 1627 * 1628 * @return the configuration for the given path 1629 */ 1630 protected CmsADEConfigData internalLookupConfiguration(CmsObject cms, String rootPath) { 1631 1632 boolean online = (null == cms) || isOnline(cms); 1633 CmsADEConfigCacheState state = getCacheState(online); 1634 return state.lookupConfiguration(rootPath); 1635 } 1636 1637 /** 1638 * Returns true if the project set in the CmsObject is the Online project.<p> 1639 * 1640 * @param cms the CMS context to check 1641 * 1642 * @return true if the project set in the CMS context is the Online project 1643 */ 1644 private boolean isOnline(CmsObject cms) { 1645 1646 return cms.getRequestContext().getCurrentProject().isOnlineProject(); 1647 } 1648 1649 /** 1650 * Saves an element list to the user additional infos.<p> 1651 * 1652 * @param cms the cms context 1653 * @param elementList the element list 1654 * @param listKey the list key 1655 * 1656 * @throws CmsException if something goes wrong 1657 */ 1658 private void saveElementList(CmsObject cms, List<CmsContainerElementBean> elementList, String listKey) 1659 throws CmsException { 1660 1661 // limit the favorite list size to avoid the additional info size limit 1662 if (elementList.size() > DEFAULT_ELEMENT_LIST_SIZE) { 1663 elementList = elementList.subList(0, DEFAULT_ELEMENT_LIST_SIZE); 1664 } 1665 1666 JSONArray data = new JSONArray(); 1667 1668 Set<String> excludedSettings = new HashSet<String>(); 1669 // do not store the template contexts, since dragging an element into the page which might be invisible 1670 // doesn't make sense 1671 excludedSettings.add(CmsTemplateContextInfo.SETTING); 1672 1673 for (CmsContainerElementBean element : elementList) { 1674 data.put(elementToJson(element, excludedSettings)); 1675 } 1676 CmsUser user = cms.getRequestContext().getCurrentUser(); 1677 user.setAdditionalInfo(listKey, data.toString()); 1678 cms.writeUser(user); 1679 } 1680}