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 * Gets the content types configured in any sitemap configuations. 414 * 415 * @param online true if the types for the Online project should be fetched 416 * @return the set of content types 417 */ 418 public Set<String> getContentTypeNames(boolean online) { 419 420 CmsConfigurationCache cache = online ? m_onlineCache : m_offlineCache; 421 return cache.getState().getContentTypes(); 422 423 } 424 425 /** 426 * Reads the current element bean from the request.<p> 427 * 428 * @param req the servlet request 429 * 430 * @return the element bean 431 * 432 * @throws CmsException if no current element is set 433 */ 434 public CmsContainerElementBean getCurrentElement(ServletRequest req) throws CmsException { 435 436 CmsJspStandardContextBean sCBean = CmsJspStandardContextBean.getInstance(req); 437 CmsContainerElementBean element = sCBean.getElement(); 438 if (element == null) { 439 throw new CmsException( 440 Messages.get().container( 441 Messages.ERR_READING_ELEMENT_FROM_REQUEST_1, 442 sCBean.getRequestContext().getUri())); 443 } 444 return element; 445 } 446 447 /** 448 * Gets the detail id cache for the Online or Offline projects.<p> 449 * 450 * @param online if true, gets the Online project detail id 451 * 452 * @return the detail name cache 453 */ 454 public CmsDetailNameCache getDetailIdCache(boolean online) { 455 456 return online ? m_onlineDetailIdCache : m_offlineDetailIdCache; 457 } 458 459 /** 460 * Gets the detail page information for everything.<p> 461 * 462 * @param cms the current CMS context 463 * 464 * @return the list with all the detail page information 465 */ 466 public List<DetailInfo> getDetailInfo(CmsObject cms) { 467 468 return getCacheState(isOnline(cms)).getDetailInfosForSubsites(cms); 469 } 470 471 /** 472 * Gets the detail page for a content element.<p> 473 * 474 * @param cms the CMS context 475 * @param pageRootPath the element's root path 476 * @param originPath the path in which the the detail page is being requested 477 * 478 * @return the detail page for the content element 479 */ 480 public String getDetailPage(CmsObject cms, String pageRootPath, String originPath) { 481 482 return getDetailPage(cms, pageRootPath, originPath, null); 483 } 484 485 /** 486 * Gets the detail page for a content element.<p> 487 * 488 * @param cms the CMS context 489 * @param rootPath the element's root path 490 * @param linkSource the path in which the the detail page is being requested 491 * @param targetDetailPage the target detail page to use 492 * 493 * @return the detail page for the content element 494 */ 495 public String getDetailPage(CmsObject cms, String rootPath, String linkSource, String targetDetailPage) { 496 497 return getDetailPageHandler().getDetailPage(cms, rootPath, linkSource, targetDetailPage); 498 } 499 500 /** 501 * Gets the detail page finder.<p> 502 * 503 * @return the detail page finder 504 */ 505 public I_CmsDetailPageHandler getDetailPageHandler() { 506 507 return m_detailPageHandler; 508 } 509 510 /** 511 * Returns the main detail pages for a type in all of the VFS tree.<p> 512 * 513 * @param cms the current CMS context 514 * @param type the resource type name 515 * @return a list of detail page root paths 516 */ 517 public List<String> getDetailPages(CmsObject cms, String type) { 518 519 CmsConfigurationCache cache = isOnline(cms) ? m_onlineCache : m_offlineCache; 520 return cache.getState().getDetailPages(type); 521 } 522 523 /** 524 * Gets the set of types for which detail pages are defined.<p> 525 * 526 * @param cms the current CMS context 527 * 528 * @return the set of types for which detail pages are defined 529 */ 530 public Set<String> getDetailPageTypes(CmsObject cms) { 531 532 return getCacheState(isOnline(cms)).getDetailPageTypes(); 533 } 534 535 /** 536 * Returns the element settings for a given resource.<p> 537 * 538 * @param cms the current cms context 539 * @param resource the resource 540 * 541 * @return the element settings for a given resource 542 * 543 * @throws CmsException if something goes wrong 544 */ 545 public Map<String, CmsXmlContentProperty> getElementSettings(CmsObject cms, CmsResource resource) 546 throws CmsException { 547 548 if (CmsResourceTypeXmlContent.isXmlContent(resource)) { 549 Map<String, CmsXmlContentProperty> result = new LinkedHashMap<String, CmsXmlContentProperty>(); 550 Map<String, CmsXmlContentProperty> settings = CmsXmlContentDefinition.getContentHandlerForResource( 551 cms, 552 resource).getSettings(cms, resource); 553 result.putAll(settings); 554 return CmsXmlContentPropertyHelper.copyPropertyConfiguration(result); 555 } 556 return Collections.<String, CmsXmlContentProperty> emptyMap(); 557 } 558 559 /** 560 * Returns the available element views.<p> 561 * 562 * @param cms the cms context 563 * 564 * @return the element views 565 */ 566 public Map<CmsUUID, CmsElementView> getElementViews(CmsObject cms) { 567 568 CmsConfigurationCache cache = getCache(isOnline(cms)); 569 List<CmsElementView> viewList = Lists.newArrayList(); 570 viewList.addAll(cache.getState().getElementViews().values()); 571 viewList.addAll(OpenCms.getWorkplaceManager().getExplorerTypeViews().values()); 572 Collections.sort(viewList, new ElementViewComparator()); 573 Map<CmsUUID, CmsElementView> result = Maps.newLinkedHashMap(); 574 for (CmsElementView viewValue : viewList) { 575 result.put(viewValue.getId(), viewValue); 576 } 577 return result; 578 } 579 580 /** 581 * Gets the element view configuration resource type.<p> 582 * 583 * @return the element view configuration resource type 584 */ 585 public I_CmsResourceType getElementViewType() { 586 587 return m_elementViewType; 588 } 589 590 /** 591 * Returns the favorite list, or creates it if not available.<p> 592 * 593 * @param cms the cms context 594 * 595 * @return the favorite list 596 * 597 * @throws CmsException if something goes wrong 598 */ 599 public List<CmsContainerElementBean> getFavoriteList(CmsObject cms) throws CmsException { 600 601 CmsUser user = cms.getRequestContext().getCurrentUser(); 602 Object obj = user.getAdditionalInfo(ADDINFO_ADE_FAVORITE_LIST); 603 604 List<CmsContainerElementBean> favList = new ArrayList<CmsContainerElementBean>(); 605 if (obj instanceof String) { 606 try { 607 JSONArray array = new JSONArray((String)obj); 608 for (int i = 0; i < array.length(); i++) { 609 try { 610 favList.add(elementFromJson(array.getJSONObject(i))); 611 } catch (Throwable e) { 612 // should never happen, catches wrong or no longer existing values 613 LOG.warn(e.getLocalizedMessage()); 614 } 615 } 616 } catch (Throwable e) { 617 // should never happen, catches json parsing 618 LOG.warn(e.getLocalizedMessage()); 619 } 620 } else { 621 // save to be better next time 622 saveFavoriteList(cms, favList); 623 } 624 625 return favList; 626 } 627 628 /** 629 * Returns the settings configured for the given formatter which should be editable via ADE.<p> 630 * 631 * @param cms the cms context 632 * @param config the sitemap configuration 633 * @param mainFormatter the formatter 634 * @param res the element resource 635 * @param locale the content locale 636 * @param req the current request, if available 637 * 638 * @return the settings configured for the given formatter 639 */ 640 public Map<String, CmsXmlContentProperty> getFormatterSettings( 641 CmsObject cms, 642 CmsADEConfigData config, 643 I_CmsFormatterBean mainFormatter, 644 CmsResource res, 645 Locale locale, 646 ServletRequest req) { 647 648 Map<String, CmsXmlContentProperty> result = new LinkedHashMap<String, CmsXmlContentProperty>(); 649 Visibility defaultVisibility = Visibility.elementAndParentIndividual; 650 if (mainFormatter != null) { 651 for (Entry<String, CmsXmlContentProperty> entry : mainFormatter.getSettings(config).entrySet()) { 652 Visibility visibility = entry.getValue().getVisibility(defaultVisibility); 653 if (visibility.isVisibleOnElement()) { 654 result.put(entry.getKey(), entry.getValue()); 655 } 656 } 657 if (mainFormatter.hasNestedFormatterSettings()) { 658 List<I_CmsFormatterBean> nestedFormatters = getNestedFormatters(cms, config, res, locale, req); 659 if (nestedFormatters != null) { 660 for (I_CmsFormatterBean formatter : nestedFormatters) { 661 for (Entry<String, CmsXmlContentProperty> entry : formatter.getSettings(config).entrySet()) { 662 Visibility visibility = entry.getValue().getVisibility(defaultVisibility); 663 switch (visibility) { 664 case parentShared: 665 case elementAndParentShared: 666 result.put(entry.getKey(), entry.getValue()); 667 break; 668 case elementAndParentIndividual: 669 case parentIndividual: 670 String settingName = formatter.getKeyOrId() + "_" + entry.getKey(); 671 CmsXmlContentProperty settingConf = entry.getValue().withName(settingName); 672 result.put(settingName, settingConf); 673 break; 674 default: 675 break; 676 } 677 } 678 } 679 } 680 } 681 } 682 return result; 683 } 684 685 /** 686 * Returns the inheritance state for the given inheritance name and resource.<p> 687 * 688 * @param cms the current cms context 689 * @param resource the resource 690 * @param name the inheritance name 691 * 692 * @return the inheritance state 693 */ 694 public CmsInheritedContainerState getInheritedContainerState(CmsObject cms, CmsResource resource, String name) { 695 696 String rootPath = resource.getRootPath(); 697 if (!resource.isFolder()) { 698 rootPath = CmsResource.getParentFolder(rootPath); 699 } 700 CmsInheritedContainerState result = new CmsInheritedContainerState(); 701 boolean online = isOnline(cms); 702 CmsContainerConfigurationCache cache = online 703 ? m_onlineContainerConfigurationCache 704 : m_offlineContainerConfigurationCache; 705 result.addConfigurations(cache, rootPath, name); 706 return result; 707 708 } 709 710 /** 711 * Returns the inheritance state for the given inheritance name and root path.<p> 712 * 713 * @param cms the current cms context 714 * @param rootPath the root path 715 * @param name the inheritance name 716 * 717 * @return the inheritance state 718 * 719 * @throws CmsException if something goes wrong 720 */ 721 public CmsInheritedContainerState getInheritedContainerState(CmsObject cms, String rootPath, String name) 722 throws CmsException { 723 724 String oldSiteRoot = cms.getRequestContext().getSiteRoot(); 725 try { 726 cms.getRequestContext().setSiteRoot(""); 727 CmsResource resource = cms.readResource(rootPath); 728 return getInheritedContainerState(cms, resource, name); 729 } finally { 730 cms.getRequestContext().setSiteRoot(oldSiteRoot); 731 } 732 } 733 734 /** 735 * Gets the maximum sitemap depth.<p> 736 * 737 * @return the maximum sitemap depth 738 */ 739 public int getMaxSitemapDepth() { 740 741 return 20; 742 } 743 744 /** 745 * Gets the module configuration resource type.<p> 746 * 747 * @return the module configuration resource type 748 */ 749 public I_CmsResourceType getModuleConfigurationType() { 750 751 return m_moduleConfigType; 752 } 753 754 /** 755 * Returns the nested formatters of the given resource.<p> 756 * 757 * @param cms the cms context 758 * @param config the sitemap configuration 759 * @param res the resource 760 * @param locale the content locale 761 * @param req the request, if available 762 * 763 * @return the nested formatters 764 */ 765 public List<I_CmsFormatterBean> getNestedFormatters( 766 CmsObject cms, 767 CmsADEConfigData config, 768 CmsResource res, 769 Locale locale, 770 ServletRequest req) { 771 772 List<I_CmsFormatterBean> result = null; 773 if (CmsResourceTypeXmlContent.isXmlContent(res)) { 774 CmsResourceTypeXmlContent type = (CmsResourceTypeXmlContent)OpenCms.getResourceManager().getResourceType( 775 res); 776 String schema = type.getSchema(); 777 try { 778 CmsXmlContentDefinition contentDefinition = CmsXmlContentDefinition.unmarshal(cms, schema); 779 // get the content handler for the resource type to create 780 I_CmsXmlContentHandler handler = contentDefinition.getContentHandler(); 781 if (handler.hasNestedFormatters()) { 782 result = new ArrayList<I_CmsFormatterBean>(); 783 for (String formatterId : handler.getNestedFormatters(cms, res, locale, req)) { 784 I_CmsFormatterBean formatter = config.findFormatter(formatterId); 785 if (formatter != null) { 786 result.add(formatter); 787 } 788 } 789 } 790 } catch (CmsXmlException e) { 791 LOG.error(e.getMessage(), e); 792 } 793 } 794 return result; 795 } 796 797 /** 798 * Gets ADE parameters.<p> 799 * 800 * @param cms the current CMS context 801 * @return the ADE parameters for the current user 802 */ 803 public Map<String, String> getParameters(CmsObject cms) { 804 805 Map<String, String> result = new LinkedHashMap<String, String>(m_parameters); 806 if (cms != null) { 807 String userParamsStr = (String)(cms.getRequestContext().getCurrentUser().getAdditionalInfo().get( 808 "ADE_PARAMS")); 809 if (userParamsStr != null) { 810 Map<String, String> userParams = CmsStringUtil.splitAsMap(userParamsStr, "|", ":"); 811 result.putAll(userParams); 812 } 813 } 814 return result; 815 } 816 817 /** 818 * Gets the content element type for the given path's parent folder. 819 * 820 * @param online true if we want to use the Online project's configuration 821 * @param rootPath the root path of a content 822 * 823 * @return the parent folder type name, or null if none is defined 824 */ 825 public String getParentFolderType(boolean online, String rootPath) { 826 827 return getCacheState(online).getParentFolderType(rootPath); 828 829 } 830 831 /** 832 * Returns the permission info for the given resource.<p> 833 * 834 * @param cms the cms context 835 * @param resource the resource 836 * @param contextPath the context path 837 * 838 * @return the permission info 839 * 840 * @throws CmsException if checking the permissions fails 841 */ 842 public CmsPermissionInfo getPermissionInfo(CmsObject cms, CmsResource resource, String contextPath) 843 throws CmsException { 844 845 boolean hasView = cms.hasPermissions( 846 resource, 847 CmsPermissionSet.ACCESS_VIEW, 848 false, 849 CmsResourceFilter.ALL.addRequireVisible()); 850 boolean hasWrite = false; 851 if (hasView) { 852 try { 853 I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(resource.getTypeId()); 854 CmsExplorerTypeSettings settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting( 855 type.getTypeName()); 856 hasView = (settings == null) 857 || settings.getAccess().getPermissions(cms, resource).requiresViewPermission(); 858 if (hasView 859 && CmsResourceTypeXmlContent.isXmlContent(resource) 860 && !CmsResourceTypeXmlContainerPage.isContainerPage(resource)) { 861 if (contextPath == null) { 862 contextPath = resource.getRootPath(); 863 } 864 CmsResourceTypeConfig localConfigData = lookupConfigurationWithCache( 865 cms, 866 contextPath).getResourceType(type.getTypeName()); 867 if (localConfigData != null) { 868 Map<CmsUUID, CmsElementView> elementViews = getElementViews(cms); 869 hasView = elementViews.containsKey(localConfigData.getElementView()) 870 && elementViews.get(localConfigData.getElementView()).hasPermission(cms, resource); 871 } 872 } 873 // the user may only have write permissions if he is allowed to view the resource 874 hasWrite = hasView 875 && cms.hasPermissions( 876 resource, 877 CmsPermissionSet.ACCESS_WRITE, 878 false, 879 CmsResourceFilter.IGNORE_EXPIRATION) 880 && ((settings == null) 881 || settings.getAccess().getPermissions(cms, resource).requiresWritePermission()); 882 } catch (CmsLoaderException e) { 883 LOG.warn(e.getLocalizedMessage(), e); 884 hasWrite = false; 885 } 886 } 887 888 if (hasWrite && isEditorRestricted(cms, resource)) { 889 hasWrite = false; 890 } 891 892 String noEdit = new CmsResourceUtil(cms, resource).getNoEditReason( 893 OpenCms.getWorkplaceManager().getWorkplaceLocale(cms), 894 true); 895 896 boolean isFunction = false; 897 for (String type : new String[] {"function", CmsResourceTypeFunctionConfig.TYPE_NAME}) { 898 if (OpenCms.getResourceManager().matchResourceType(type, resource.getTypeId())) { 899 isFunction = true; 900 break; 901 } 902 } 903 if (isFunction) { 904 Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms); 905 noEdit = Messages.get().getBundle(locale).key(Messages.GUI_CANT_EDIT_FUNCTIONS_0); 906 } 907 908 return new CmsPermissionInfo(hasView, hasWrite, noEdit); 909 } 910 911 /** 912 * Gets a map of plugin wrappers for the given site path. 913 * 914 * <p>This *only* includes plugins defined in site plugins active on the given path, not those referenced in formatters. 915 * 916 * @param cms the CMS context 917 * @param path the path for which to get the plugins 918 * 919 * @return the map of plugin wrappers, with the plugin groups as keys 920 */ 921 public Map<String, List<CmsTemplatePluginWrapper>> getPluginsForPath(CmsObject cms, String path) { 922 923 CmsADEConfigData config = lookupConfigurationWithCache(cms, cms.getRequestContext().addSiteRoot(path)); 924 925 Multimap<String, CmsTemplatePlugin> plugins = CmsTemplatePluginFinder.getActiveTemplatePluginsFromSitePlugins( 926 config); 927 Map<String, List<CmsTemplatePluginWrapper>> result = new HashMap<>(); 928 for (String key : plugins.keySet()) { 929 List<CmsTemplatePluginWrapper> wrappers = plugins.get(key).stream().map( 930 plugin -> new CmsTemplatePluginWrapper(cms, plugin)).collect(Collectors.toList()); 931 result.put(key, Collections.unmodifiableList(wrappers)); 932 } 933 return Collections.unmodifiableMap(result); 934 935 } 936 937 /** 938 * Gets the raw configured detail page information, with no existence checks or path correction. 939 * 940 * @param cms the CMS context 941 * @return the list of raw detail page info beans 942 */ 943 public List<CmsDetailPageInfo> getRawDetailPages(CmsObject cms) { 944 945 return getCache(cms.getRequestContext().getCurrentProject().isOnlineProject()).getRawDetailPages(); 946 } 947 948 /** 949 * Returns the favorite list, or creates it if not available.<p> 950 * 951 * @param cms the cms context 952 * 953 * @return the favorite list 954 * 955 * @throws CmsException if something goes wrong 956 */ 957 public List<CmsContainerElementBean> getRecentList(CmsObject cms) throws CmsException { 958 959 CmsUser user = cms.getRequestContext().getCurrentUser(); 960 Object obj = user.getAdditionalInfo(ADDINFO_ADE_RECENT_LIST); 961 962 List<CmsContainerElementBean> recentList = new ArrayList<CmsContainerElementBean>(); 963 if (obj instanceof String) { 964 try { 965 JSONArray array = new JSONArray((String)obj); 966 for (int i = 0; i < array.length(); i++) { 967 try { 968 recentList.add(elementFromJson(array.getJSONObject(i))); 969 } catch (Throwable e) { 970 // should never happen, catches wrong or no longer existing values 971 LOG.warn(e.getLocalizedMessage()); 972 } 973 } 974 } catch (Throwable e) { 975 // should never happen, catches json parsing 976 LOG.warn(e.getLocalizedMessage()); 977 } 978 } else { 979 // save to be better next time 980 saveRecentList(cms, recentList); 981 } 982 983 return recentList; 984 } 985 986 /** 987 * Gets the sitemap configuration resource type.<p> 988 * 989 * @return the resource type for sitemap configurations 990 */ 991 public I_CmsResourceType getSitemapConfigurationType() { 992 993 return m_configType; 994 } 995 996 /** 997 * Returns all sub sites below the given path.<p> 998 * 999 * @param cms the cms context 1000 * @param subSiteRoot the sub site root path 1001 * 1002 * @return the sub site root paths 1003 */ 1004 public List<String> getSubSitePaths(CmsObject cms, String subSiteRoot) { 1005 1006 List<String> result = new ArrayList<String>(); 1007 String normalizedRootPath = CmsStringUtil.joinPaths("/", subSiteRoot, "/"); 1008 CmsADEConfigCacheState state = getCacheState(isOnline(cms)); 1009 Set<String> siteConfigurationPaths = state.getSiteConfigurationPaths(); 1010 for (String path : siteConfigurationPaths) { 1011 if ((path.length() > normalizedRootPath.length()) && path.startsWith(normalizedRootPath)) { 1012 result.add(path); 1013 } 1014 } 1015 return result; 1016 } 1017 1018 /** 1019 * Tries to get the subsite root for a given resource root path.<p> 1020 * 1021 * @param cms the current CMS context 1022 * @param rootPath the root path for which the subsite root should be found 1023 * 1024 * @return the subsite root 1025 */ 1026 public String getSubSiteRoot(CmsObject cms, String rootPath) { 1027 1028 CmsADEConfigData configData = lookupConfiguration(cms, rootPath); 1029 String basePath = configData.getBasePath(); 1030 String siteRoot = OpenCms.getSiteManager().getSiteRoot(rootPath); 1031 if (siteRoot == null) { 1032 siteRoot = ""; 1033 } 1034 if ((basePath == null) || !basePath.startsWith(siteRoot)) { 1035 // the subsite root should always be below the site root 1036 return siteRoot; 1037 } else { 1038 return basePath; 1039 } 1040 } 1041 1042 /** 1043 * Gets the subsites to be displayed in the site selector. 1044 * 1045 * @param online true if we want the subsites for the Online project 1046 * 1047 * @return the subsites to be displayed in the site selector 1048 */ 1049 public List<String> getSubsitesForSiteSelector(boolean online) { 1050 1051 return getCacheState(online).getSubsitesForSiteSelector(); 1052 1053 } 1054 1055 /** 1056 * Gets the table of upload warnings. 1057 * 1058 * @return the table of upload warnings 1059 */ 1060 public CmsUploadWarningTable getUploadWarningTable() { 1061 1062 return m_uploadWarningTable; 1063 } 1064 1065 /** 1066 * Processes a HTML redirect content.<p> 1067 * 1068 * This needs to be in the ADE manager because the user for whom the HTML redirect is being loaded 1069 * does not necessarily have read permissions for the redirect target, so we read the redirect target 1070 * with admin privileges.<p> 1071 * 1072 * @param userCms the CMS context of the current user 1073 * @param request the servlet request 1074 * @param response the servlet response 1075 * @param htmlRedirect the path of the HTML redirect resource 1076 * 1077 * @throws CmsException if something goes wrong 1078 */ 1079 public void handleHtmlRedirect( 1080 CmsObject userCms, 1081 HttpServletRequest request, 1082 HttpServletResponse response, 1083 String htmlRedirect) 1084 throws CmsException { 1085 1086 CmsObject cms = OpenCms.initCmsObject(m_offlineCms); 1087 CmsRequestContext userContext = userCms.getRequestContext(); 1088 CmsRequestContext currentContext = cms.getRequestContext(); 1089 currentContext.setCurrentProject(userContext.getCurrentProject()); 1090 currentContext.setSiteRoot(userContext.getSiteRoot()); 1091 currentContext.setLocale(userContext.getLocale()); 1092 currentContext.setUri(userContext.getUri()); 1093 1094 CmsFile file = cms.readFile(htmlRedirect); 1095 CmsXmlContent content = CmsXmlContentFactory.unmarshal(cms, file); 1096 1097 // find out the locale to use for reading values from the redirect 1098 List<Locale> candidates = new ArrayList<Locale>(); 1099 candidates.add(currentContext.getLocale()); 1100 candidates.add(CmsLocaleManager.getDefaultLocale()); 1101 candidates.add(Locale.ENGLISH); 1102 candidates.addAll(content.getLocales()); 1103 Locale contentLocale = currentContext.getLocale(); 1104 for (Locale candidateLocale : candidates) { 1105 if (content.hasLocale(candidateLocale)) { 1106 contentLocale = candidateLocale; 1107 break; 1108 } 1109 } 1110 1111 String typeValue = content.getValue(N_TYPE, contentLocale).getStringValue(cms); 1112 String lnkUri = ""; 1113 Integer errorCode; 1114 if ("sublevel".equals(typeValue)) { 1115 // use the nav builder to get the first sub level entry 1116 CmsJspNavBuilder navBuilder = new CmsJspNavBuilder(cms); 1117 if (navBuilder.getNavigationForFolder().size() > 0) { 1118 CmsJspNavElement target = navBuilder.getNavigationForFolder().get(0); 1119 lnkUri = CmsJspTagLink.linkTagAction(target.getResourceName(), request); 1120 errorCode = Integer.valueOf(HttpServletResponse.SC_MOVED_TEMPORARILY); 1121 } else { 1122 // send error 404 if no sub entry available 1123 errorCode = Integer.valueOf(HttpServletResponse.SC_NOT_FOUND); 1124 } 1125 } else { 1126 I_CmsXmlContentValue contentValue = content.getValue(N_LINK, contentLocale); 1127 if (contentValue != null) { 1128 String linkValue = contentValue.getStringValue(cms); 1129 lnkUri = OpenCms.getLinkManager().substituteLinkForUnknownTarget(cms, linkValue); 1130 try { 1131 errorCode = Integer.valueOf(typeValue); 1132 } catch (NumberFormatException e) { 1133 LOG.error(e.getMessage(), e); 1134 // fall back to default 1135 errorCode = Integer.valueOf(307); 1136 } 1137 } else { 1138 // send error 404 if no link value is set 1139 errorCode = Integer.valueOf(HttpServletResponse.SC_NOT_FOUND); 1140 } 1141 } 1142 if (!currentContext.getCurrentProject().isOnlineProject()) { 1143 // permanent redirects are confusing and not useful in the Offline project because they are stored 1144 // by the browser based on the host name, not the site the user is working in. 1145 if (errorCode.intValue() == HttpServletResponse.SC_MOVED_PERMANENTLY) { 1146 errorCode = Integer.valueOf(HttpServletResponse.SC_MOVED_TEMPORARILY); 1147 } 1148 } 1149 request.setAttribute(CmsRequestUtil.ATTRIBUTE_ERRORCODE, errorCode); 1150 response.setHeader("Location", CmsEncoder.convertHostToPunycode(lnkUri)); 1151 response.setHeader("Connection", "close"); 1152 response.setStatus(errorCode.intValue()); 1153 } 1154 1155 /** 1156 * Initializes the configuration by reading all configuration files and caching their data.<p> 1157 */ 1158 public synchronized void initialize() { 1159 1160 // no need to try initialization in case the 'org.opencms.base' is not present and the contained resource types missing 1161 if ((m_initStatus == Status.notInitialized) && OpenCms.getModuleManager().hasModule(MODULE_NAME_ADE_CONFIG)) { 1162 try { 1163 CmsLog.INIT.info(". Initializing the ADE configuration, this may take a while..."); 1164 m_initStatus = Status.initializing; 1165 m_configType = OpenCms.getResourceManager().getResourceType(CONFIG_TYPE); 1166 m_moduleConfigType = OpenCms.getResourceManager().getResourceType(MODULE_CONFIG_TYPE); 1167 m_elementViewType = OpenCms.getResourceManager().getResourceType(ELEMENT_VIEW_TYPE); 1168 CmsProject temp = getTempfileProject(m_onlineCms); 1169 m_offlineCms = OpenCms.initCmsObject(m_onlineCms); 1170 m_offlineCms.getRequestContext().setCurrentProject(temp); 1171 m_onlineCache = new CmsConfigurationCache( 1172 m_onlineCms, 1173 m_configType, 1174 m_moduleConfigType, 1175 m_elementViewType); 1176 m_offlineCache = new CmsConfigurationCache( 1177 m_offlineCms, 1178 m_configType, 1179 m_moduleConfigType, 1180 m_elementViewType); 1181 CmsLog.INIT.info(". Reading online configuration..."); 1182 m_onlineCache.initialize(); 1183 CmsLog.INIT.info(". Reading offline configuration..."); 1184 m_offlineCache.initialize(); 1185 m_onlineContainerConfigurationCache = new CmsContainerConfigurationCache( 1186 m_onlineCms, 1187 "online inheritance groups"); 1188 m_offlineContainerConfigurationCache = new CmsContainerConfigurationCache( 1189 m_offlineCms, 1190 "offline inheritance groups"); 1191 CmsLog.INIT.info(". Reading online inherited container configurations..."); 1192 m_onlineContainerConfigurationCache.initialize(); 1193 CmsLog.INIT.info(". Reading offline inherited container configurations..."); 1194 m_offlineContainerConfigurationCache.initialize(); 1195 m_offlineFormatterCache = new CmsFormatterConfigurationCache(m_offlineCms, "offline formatters"); 1196 m_onlineFormatterCache = new CmsFormatterConfigurationCache(m_onlineCms, "online formatters"); 1197 CmsLog.INIT.info(". Reading online formatter configurations..."); 1198 m_onlineFormatterCache.initialize(); 1199 CmsLog.INIT.info(". Reading offline formatter configurations..."); 1200 m_offlineFormatterCache.initialize(); 1201 1202 m_offlineDetailIdCache = new CmsDetailNameCache(m_offlineCms); 1203 m_onlineDetailIdCache = new CmsDetailNameCache(m_onlineCms); 1204 CmsLog.INIT.info(". Initializing online detail name cache..."); 1205 m_onlineDetailIdCache.initialize(); 1206 CmsLog.INIT.info(". Initializing offline detail name cache..."); 1207 m_offlineDetailIdCache.initialize(); 1208 1209 CmsGlobalConfigurationCacheEventHandler handler = new CmsGlobalConfigurationCacheEventHandler( 1210 m_onlineCms); 1211 handler.addCache(m_offlineCache, m_onlineCache, "ADE configuration cache"); 1212 handler.addCache( 1213 m_offlineContainerConfigurationCache, 1214 m_onlineContainerConfigurationCache, 1215 "Inherited container cache"); 1216 handler.addCache(m_offlineFormatterCache, m_onlineFormatterCache, "formatter configuration cache"); 1217 handler.addCache(m_offlineDetailIdCache, m_onlineDetailIdCache, "Detail ID cache"); 1218 OpenCms.getEventManager().addCmsEventListener(handler); 1219 CmsLog.INIT.info(". Done initializing the ADE configuration."); 1220 m_initStatus = Status.initialized; 1221 } catch (CmsException e) { 1222 m_initStatus = Status.notInitialized; 1223 LOG.error(e.getLocalizedMessage(), e); 1224 } 1225 m_detailPageHandler.initialize(m_offlineCms, m_onlineCms); 1226 } 1227 } 1228 1229 /** 1230 * Checks whether the given resource is configured as a detail page.<p> 1231 * 1232 * @param cms the current CMS context 1233 * @param resource the resource which should be tested 1234 * 1235 * @return true if the resource is configured as a detail page 1236 */ 1237 public boolean isDetailPage(CmsObject cms, CmsResource resource) { 1238 1239 return getCache(isOnline(cms)).isDetailPage(cms, resource); 1240 } 1241 1242 /** 1243 * Checks if the user should be prevented from editing a file. 1244 * 1245 * <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. 1246 * 1247 * @param cms the CMS context 1248 * @param res the resource to check 1249 * @return true if the user should be prevented from editing the file 1250 */ 1251 public boolean isEditorRestricted(CmsObject cms, CmsResource res) { 1252 1253 if (OpenCms.getResourceManager().matchResourceType(CONFIG_TYPE, res.getTypeId())) { 1254 CmsRole role = getRoleForSitemapConfigEditing(); 1255 if ((role != null) && !OpenCms.getRoleManager().hasRoleForResource(cms, role, res)) { 1256 return true; 1257 } 1258 } 1259 return false; 1260 } 1261 1262 /** 1263 * Checks whether the ADE manager is initialized (this should usually be the case except during the setup).<p> 1264 * 1265 * @return true if the ADE manager is initialized 1266 */ 1267 public boolean isInitialized() { 1268 1269 return m_initStatus == Status.initialized; 1270 } 1271 1272 /** 1273 * Returns the show editor help flag.<p> 1274 * 1275 * @param cms the cms context 1276 * 1277 * @return the show editor help flag 1278 */ 1279 public boolean isShowEditorHelp(CmsObject cms) { 1280 1281 CmsUser user = cms.getRequestContext().getCurrentUser(); 1282 String showHelp = (String)user.getAdditionalInfo(ADDINFO_ADE_SHOW_EDITOR_HELP); 1283 return CmsStringUtil.isEmptyOrWhitespaceOnly(showHelp) || Boolean.parseBoolean(showHelp); 1284 } 1285 1286 /** 1287 * Looks up the configuration data for a given sitemap path.<p> 1288 * 1289 * @param cms the current CMS context 1290 * @param rootPath the root path for which the configuration data should be looked up 1291 * 1292 * @return the configuration data 1293 */ 1294 public CmsADEConfigData lookupConfiguration(CmsObject cms, String rootPath) { 1295 1296 CmsADEConfigData configData = internalLookupConfiguration(cms, rootPath); 1297 return configData; 1298 } 1299 1300 /** 1301 * Looks up the configuration data for a given sitemap path, but uses a thread-local cache for the current request for efficiency. 1302 * 1303 * @param cms the current CMS context 1304 * @param rootPath the root path for which the configuration data should be looked up 1305 * 1306 * @return the configuration data 1307 */ 1308 public CmsADEConfigData lookupConfigurationWithCache(CmsObject cms, String rootPath) { 1309 1310 boolean online = (cms == null) || cms.getRequestContext().getCurrentProject().isOnlineProject(); 1311 String cacheKey = "" + online + ":" + rootPath; 1312 OpenCmsServlet.RequestCache context = OpenCmsServlet.getRequestCache(); 1313 CmsADEConfigData result = null; 1314 if (context != null) { 1315 result = context.getCachedConfig(cacheKey); 1316 } 1317 if (result == null) { 1318 result = internalLookupConfiguration(cms, rootPath); 1319 if (context != null) { 1320 context.setCachedConfig(cacheKey, result); 1321 } 1322 } 1323 return result; 1324 } 1325 1326 /** 1327 * Reloads the configuration.<p> 1328 * 1329 * Normally you shouldn't call this directly since the event handlers take care of updating the configuration. 1330 */ 1331 public void refresh() { 1332 1333 m_onlineCache.initialize(); 1334 m_offlineCache.initialize(); 1335 } 1336 1337 /** 1338 * Saves a list of detail pages.<p> 1339 * @param cms the cms context 1340 * @param rootPath the root path 1341 * @param detailPages the detail pages 1342 * @param newId the id to use for new detail pages without an id 1343 * @return true if the detail pages could be successfully saved 1344 * 1345 * @throws CmsException if something goes wrong 1346 */ 1347 public boolean saveDetailPages(CmsObject cms, String rootPath, List<CmsDetailPageInfo> detailPages, CmsUUID newId) 1348 throws CmsException { 1349 1350 CmsADEConfigData configData = lookupConfiguration(cms, rootPath); 1351 CmsDetailPageConfigurationWriter configWriter; 1352 String originalSiteRoot = cms.getRequestContext().getSiteRoot(); 1353 try { 1354 cms.getRequestContext().setSiteRoot(""); 1355 if (configData.isModuleConfiguration()) { 1356 return false; 1357 } 1358 CmsResource configFile = configData.getResource(); 1359 configWriter = new CmsDetailPageConfigurationWriter(cms, configFile); 1360 configWriter.updateAndSave(detailPages, newId); 1361 return true; 1362 } finally { 1363 cms.getRequestContext().setSiteRoot(originalSiteRoot); 1364 } 1365 } 1366 1367 /** 1368 * Saves the favorite list, user based.<p> 1369 * 1370 * @param cms the cms context 1371 * @param favoriteList the element list 1372 * 1373 * @throws CmsException if something goes wrong 1374 */ 1375 public void saveFavoriteList(CmsObject cms, List<CmsContainerElementBean> favoriteList) throws CmsException { 1376 1377 saveElementList(cms, favoriteList, ADDINFO_ADE_FAVORITE_LIST); 1378 } 1379 1380 /** 1381 * Saves the inheritance container information.<p> 1382 * 1383 * @param cms the current cms context 1384 * @param pageResource the resource or parent folder 1385 * @param name the inheritance name 1386 * @param newOrder if the element have been reordered 1387 * @param elements the elements 1388 * 1389 * @throws CmsException if something goes wrong 1390 */ 1391 public void saveInheritedContainer( 1392 CmsObject cms, 1393 CmsResource pageResource, 1394 String name, 1395 boolean newOrder, 1396 List<CmsContainerElementBean> elements) 1397 throws CmsException { 1398 1399 CmsContainerConfigurationWriter writer = new CmsContainerConfigurationWriter(); 1400 writer.save(cms, name, newOrder, pageResource, elements); 1401 1402 // Inheritance groups are usually reloaded directly after saving them, 1403 // so the cache needs to be up to date after this method is called 1404 m_offlineContainerConfigurationCache.flushUpdates(); 1405 } 1406 1407 /** 1408 * Saves the inheritance container information.<p> 1409 * 1410 * @param cms the current cms context 1411 * @param sitePath the site path of the resource or parent folder 1412 * @param name the inheritance name 1413 * @param newOrder if the element have been reordered 1414 * @param elements the elements 1415 * 1416 * @throws CmsException if something goes wrong 1417 */ 1418 public void saveInheritedContainer( 1419 CmsObject cms, 1420 String sitePath, 1421 String name, 1422 boolean newOrder, 1423 List<CmsContainerElementBean> elements) 1424 throws CmsException { 1425 1426 saveInheritedContainer(cms, cms.readResource(sitePath), name, newOrder, elements); 1427 } 1428 1429 /** 1430 * Saves the favorite list, user based.<p> 1431 * 1432 * @param cms the cms context 1433 * @param recentList the element list 1434 * 1435 * @throws CmsException if something goes wrong 1436 */ 1437 public void saveRecentList(CmsObject cms, List<CmsContainerElementBean> recentList) throws CmsException { 1438 1439 saveElementList(cms, recentList, ADDINFO_ADE_RECENT_LIST); 1440 } 1441 1442 /** 1443 * Sets the show editor help flag.<p> 1444 * 1445 * @param cms the cms context 1446 * @param showHelp the show help flag 1447 * @throws CmsException if writing the user info fails 1448 */ 1449 public void setShowEditorHelp(CmsObject cms, boolean showHelp) throws CmsException { 1450 1451 CmsUser user = cms.getRequestContext().getCurrentUser(); 1452 user.setAdditionalInfo(ADDINFO_ADE_SHOW_EDITOR_HELP, String.valueOf(showHelp)); 1453 cms.writeUser(user); 1454 } 1455 1456 /** 1457 * The method which is called when the OpenCms instance is shut down.<p> 1458 */ 1459 public void shutdown() { 1460 1461 // do nothing 1462 } 1463 1464 /** 1465 * Waits until the next time the cache is updated.<p> 1466 * 1467 * @param online true if we want to wait for the online cache, false for the offline cache 1468 */ 1469 public void waitForCacheUpdate(boolean online) { 1470 1471 getCache(online).getWaitHandleForUpdateTask().enter(2 * CmsConfigurationCache.TASK_DELAY_MILLIS); 1472 } 1473 1474 /** 1475 * Waits until the formatter cache has finished updating itself.<p> 1476 * 1477 * This method is only intended for use in test cases. 1478 * 1479 * @param online true if we should wait for the online formatter cache,false for the offline cache 1480 */ 1481 public void waitForFormatterCache(boolean online) { 1482 1483 CmsFormatterConfigurationCache cache = online ? m_onlineFormatterCache : m_offlineFormatterCache; 1484 cache.waitForUpdate(); 1485 } 1486 1487 /** 1488 * Creates an element from its serialized data.<p> 1489 * 1490 * @param data the serialized data 1491 * 1492 * @return the restored element bean 1493 * 1494 * @throws JSONException if the serialized data got corrupted 1495 */ 1496 protected CmsContainerElementBean elementFromJson(JSONObject data) throws JSONException { 1497 1498 CmsUUID element = new CmsUUID(data.getString(FavListProp.ELEMENT.name().toLowerCase())); 1499 CmsUUID formatter = null; 1500 if (data.has(FavListProp.FORMATTER.name().toLowerCase())) { 1501 formatter = new CmsUUID(data.getString(FavListProp.FORMATTER.name().toLowerCase())); 1502 } 1503 Map<String, String> properties = new HashMap<String, String>(); 1504 1505 JSONObject props = data.getJSONObject(FavListProp.PROPERTIES.name().toLowerCase()); 1506 Iterator<String> keys = props.keys(); 1507 while (keys.hasNext()) { 1508 String key = keys.next(); 1509 properties.put(key, props.getString(key)); 1510 } 1511 1512 return new CmsContainerElementBean(element, formatter, properties, false); 1513 } 1514 1515 /** 1516 * Converts the given element to JSON.<p> 1517 * 1518 * @param element the element to convert 1519 * @param excludeSettings the keys of settings which should not be written to the JSON 1520 * 1521 * @return the JSON representation 1522 */ 1523 protected JSONObject elementToJson(CmsContainerElementBean element, Set<String> excludeSettings) { 1524 1525 JSONObject data = null; 1526 try { 1527 data = new JSONObject(); 1528 data.put(FavListProp.ELEMENT.name().toLowerCase(), element.getId().toString()); 1529 if (element.getFormatterId() != null) { 1530 data.put(FavListProp.FORMATTER.name().toLowerCase(), element.getFormatterId().toString()); 1531 } 1532 JSONObject properties = new JSONObject(); 1533 for (Map.Entry<String, String> entry : element.getIndividualSettings().entrySet()) { 1534 String settingKey = entry.getKey(); 1535 if (!excludeSettings.contains(settingKey)) { 1536 properties.put(entry.getKey(), entry.getValue()); 1537 } 1538 } 1539 data.put(FavListProp.PROPERTIES.name().toLowerCase(), properties); 1540 } catch (JSONException e) { 1541 // should never happen 1542 if (!LOG.isDebugEnabled()) { 1543 LOG.warn(e.getLocalizedMessage()); 1544 } 1545 LOG.debug(e.getLocalizedMessage(), e); 1546 return null; 1547 } 1548 return data; 1549 } 1550 1551 /** 1552 * Gets the configuration cache instance.<p> 1553 * 1554 * @param online true if you want the online cache, false for the offline cache 1555 * 1556 * @return the ADE configuration cache instance 1557 */ 1558 protected CmsConfigurationCache getCache(boolean online) { 1559 1560 return online ? m_onlineCache : m_offlineCache; 1561 } 1562 1563 /** 1564 * Gets the offline cache.<p> 1565 * 1566 * @return the offline configuration cache 1567 */ 1568 protected CmsConfigurationCache getOfflineCache() { 1569 1570 return m_offlineCache; 1571 } 1572 1573 /** 1574 * Gets the online cache.<p> 1575 * 1576 * @return the online configuration cache 1577 */ 1578 protected CmsConfigurationCache getOnlineCache() { 1579 1580 return m_onlineCache; 1581 } 1582 1583 /** 1584 * Gets the role necessary to edit sitemap configuration files. 1585 * 1586 * @return the role needed for editing sitemap configurations 1587 */ 1588 protected CmsRole getRoleForSitemapConfigEditing() { 1589 1590 String roleName = OpenCms.getWorkplaceManager().getSitemapConfigEditRole(); 1591 if (roleName == null) { 1592 return null; 1593 } else { 1594 if (roleName.indexOf("/") == -1) { 1595 return CmsRole.valueOfRoleName(roleName).forOrgUnit(null); 1596 } else { 1597 return CmsRole.valueOfRoleName(roleName); 1598 } 1599 } 1600 } 1601 1602 /** 1603 * Gets the root path for a given resource structure id.<p> 1604 * 1605 * @param structureId the structure id 1606 * @param online if true, the resource will be looked up in the online project ,else in the offline project 1607 * 1608 * @return the root path for the given structure id 1609 * 1610 * @throws CmsException if something goes wrong 1611 */ 1612 protected String getRootPath(CmsUUID structureId, boolean online) throws CmsException { 1613 1614 CmsConfigurationCache cache = online ? m_onlineCache : m_offlineCache; 1615 return cache.getPathForStructureId(structureId); 1616 } 1617 1618 /** 1619 * Gets a tempfile project, creating one if it doesn't exist already.<p> 1620 * 1621 * @param cms the CMS context to use 1622 * @return the tempfile project 1623 * 1624 * @throws CmsException if something goes wrong 1625 */ 1626 protected CmsProject getTempfileProject(CmsObject cms) throws CmsException { 1627 1628 try { 1629 return cms.readProject(I_CmsProjectDriver.TEMP_FILE_PROJECT_NAME); 1630 } catch (CmsException e) { 1631 return cms.createTempfileProject(); 1632 } 1633 } 1634 1635 /** 1636 * Internal configuration lookup method.<p> 1637 * 1638 * @param cms the cms context 1639 * @param rootPath the root path for which to look up the configuration 1640 * 1641 * @return the configuration for the given path 1642 */ 1643 protected CmsADEConfigData internalLookupConfiguration(CmsObject cms, String rootPath) { 1644 1645 boolean online = (null == cms) || isOnline(cms); 1646 CmsADEConfigCacheState state = getCacheState(online); 1647 return state.lookupConfiguration(rootPath); 1648 } 1649 1650 /** 1651 * Returns true if the project set in the CmsObject is the Online project.<p> 1652 * 1653 * @param cms the CMS context to check 1654 * 1655 * @return true if the project set in the CMS context is the Online project 1656 */ 1657 private boolean isOnline(CmsObject cms) { 1658 1659 return cms.getRequestContext().getCurrentProject().isOnlineProject(); 1660 } 1661 1662 /** 1663 * Saves an element list to the user additional infos.<p> 1664 * 1665 * @param cms the cms context 1666 * @param elementList the element list 1667 * @param listKey the list key 1668 * 1669 * @throws CmsException if something goes wrong 1670 */ 1671 private void saveElementList(CmsObject cms, List<CmsContainerElementBean> elementList, String listKey) 1672 throws CmsException { 1673 1674 // limit the favorite list size to avoid the additional info size limit 1675 if (elementList.size() > DEFAULT_ELEMENT_LIST_SIZE) { 1676 elementList = elementList.subList(0, DEFAULT_ELEMENT_LIST_SIZE); 1677 } 1678 1679 JSONArray data = new JSONArray(); 1680 1681 Set<String> excludedSettings = new HashSet<String>(); 1682 // do not store the template contexts, since dragging an element into the page which might be invisible 1683 // doesn't make sense 1684 excludedSettings.add(CmsTemplateContextInfo.SETTING); 1685 1686 for (CmsContainerElementBean element : elementList) { 1687 data.put(elementToJson(element, excludedSettings)); 1688 } 1689 CmsUser user = cms.getRequestContext().getCurrentUser(); 1690 user.setAdditionalInfo(listKey, data.toString()); 1691 cms.writeUser(user); 1692 } 1693}