001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://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.jsp.util; 029 030import org.opencms.ade.configuration.CmsADEConfigData; 031import org.opencms.ade.configuration.CmsADEManager; 032import org.opencms.ade.configuration.CmsFunctionReference; 033import org.opencms.ade.configuration.CmsResourceTypeConfig; 034import org.opencms.ade.configuration.plugins.CmsTemplatePlugin; 035import org.opencms.ade.configuration.plugins.CmsTemplatePluginFinder; 036import org.opencms.ade.containerpage.CmsContainerpageService; 037import org.opencms.ade.containerpage.CmsDetailOnlyContainerUtil; 038import org.opencms.ade.containerpage.CmsModelGroupHelper; 039import org.opencms.ade.containerpage.shared.CmsFormatterConfig; 040import org.opencms.ade.containerpage.shared.CmsInheritanceInfo; 041import org.opencms.ade.detailpage.CmsDetailPageInfo; 042import org.opencms.ade.detailpage.CmsDetailPageResourceHandler; 043import org.opencms.file.CmsFile; 044import org.opencms.file.CmsObject; 045import org.opencms.file.CmsProperty; 046import org.opencms.file.CmsPropertyDefinition; 047import org.opencms.file.CmsRequestContext; 048import org.opencms.file.CmsResource; 049import org.opencms.file.CmsResourceFilter; 050import org.opencms.file.CmsVfsResourceNotFoundException; 051import org.opencms.file.history.CmsHistoryResourceHandler; 052import org.opencms.file.types.CmsResourceTypeXmlContainerPage; 053import org.opencms.file.types.I_CmsResourceType; 054import org.opencms.flex.CmsFlexController; 055import org.opencms.flex.CmsFlexRequest; 056import org.opencms.gwt.shared.CmsGwtConstants; 057import org.opencms.i18n.CmsEncoder; 058import org.opencms.i18n.CmsLocaleGroupService; 059import org.opencms.i18n.CmsMessageToBundleIndex; 060import org.opencms.i18n.CmsResourceBundleLoader; 061import org.opencms.i18n.CmsVfsResourceBundle; 062import org.opencms.jsp.CmsJspBean; 063import org.opencms.jsp.CmsJspResourceWrapper; 064import org.opencms.jsp.CmsJspTagContainer; 065import org.opencms.jsp.CmsJspTagEditable; 066import org.opencms.jsp.Messages; 067import org.opencms.jsp.jsonpart.CmsJsonPartFilter; 068import org.opencms.jsp.search.config.parser.simplesearch.CmsConfigParserUtils; 069import org.opencms.loader.CmsLoaderException; 070import org.opencms.loader.CmsTemplateContextManager; 071import org.opencms.main.CmsException; 072import org.opencms.main.CmsLog; 073import org.opencms.main.CmsRuntimeException; 074import org.opencms.main.CmsSystemInfo; 075import org.opencms.main.OpenCms; 076import org.opencms.main.OpenCmsServlet; 077import org.opencms.relations.CmsCategory; 078import org.opencms.relations.CmsCategoryService; 079import org.opencms.search.galleries.CmsGalleryNameMacroResolver; 080import org.opencms.site.CmsSite; 081import org.opencms.site.CmsSiteMatcher; 082import org.opencms.staticexport.CmsLinkManager; 083import org.opencms.ui.CmsVaadinUtils; 084import org.opencms.ui.apps.A_CmsWorkplaceApp; 085import org.opencms.ui.apps.CmsEditor; 086import org.opencms.ui.apps.CmsEditorConfiguration; 087import org.opencms.ui.editors.messagebundle.CmsMessageBundleEditor; 088import org.opencms.util.CmsCollectionsGenericWrapper; 089import org.opencms.util.CmsColorContrastCalculator; 090import org.opencms.util.CmsFileUtil; 091import org.opencms.util.CmsMacroResolver; 092import org.opencms.util.CmsStringUtil; 093import org.opencms.util.CmsUUID; 094import org.opencms.workplace.galleries.CmsAjaxDownloadGallery; 095import org.opencms.workplace.galleries.CmsAjaxImageGallery; 096import org.opencms.xml.CmsXmlContentDefinition; 097import org.opencms.xml.containerpage.CmsADESessionCache; 098import org.opencms.xml.containerpage.CmsContainerBean; 099import org.opencms.xml.containerpage.CmsContainerElementBean; 100import org.opencms.xml.containerpage.CmsContainerPageBean; 101import org.opencms.xml.containerpage.CmsDynamicFunctionBean; 102import org.opencms.xml.containerpage.CmsDynamicFunctionParser; 103import org.opencms.xml.containerpage.CmsFormatterConfiguration; 104import org.opencms.xml.containerpage.CmsMetaMapping; 105import org.opencms.xml.containerpage.CmsXmlContainerPage; 106import org.opencms.xml.containerpage.CmsXmlContainerPageFactory; 107import org.opencms.xml.containerpage.I_CmsFormatterBean; 108import org.opencms.xml.content.CmsXmlContent; 109import org.opencms.xml.content.CmsXmlContentFactory; 110import org.opencms.xml.content.CmsXmlContentProperty; 111import org.opencms.xml.templatemapper.CmsTemplateMapper; 112import org.opencms.xml.types.I_CmsXmlContentValue; 113 114import java.lang.reflect.Constructor; 115import java.lang.reflect.Method; 116import java.net.URI; 117import java.net.URISyntaxException; 118import java.util.ArrayList; 119import java.util.Arrays; 120import java.util.Collection; 121import java.util.Collections; 122import java.util.Comparator; 123import java.util.HashMap; 124import java.util.HashSet; 125import java.util.List; 126import java.util.Locale; 127import java.util.Map; 128import java.util.ResourceBundle; 129import java.util.Set; 130import java.util.function.Predicate; 131import java.util.function.Supplier; 132import java.util.stream.Collectors; 133 134import javax.servlet.ServletRequest; 135import javax.servlet.http.HttpServletRequest; 136 137import org.apache.commons.collections.Transformer; 138import org.apache.commons.lang3.LocaleUtils; 139import org.apache.commons.logging.Log; 140 141import com.google.common.collect.ComparisonChain; 142import com.google.common.collect.Multimap; 143 144/** 145 * Allows convenient access to the most important OpenCms functions on a JSP page, 146 * indented to be used from a JSP with the JSTL or EL.<p> 147 * 148 * This bean is available by default in the context of an OpenCms managed JSP.<p> 149 * 150 * @since 8.0 151 */ 152public final class CmsJspStandardContextBean { 153 154 /** 155 * Container element wrapper to add some API methods.<p> 156 */ 157 public class CmsContainerElementWrapper extends CmsContainerElementBean { 158 159 CmsContainerElementBean m_wrappedElement; 160 161 /** The wrapped element instance. */ 162 private Supplier<CmsContainerElementBean> m_elementProvider; 163 164 /** Cached formatter key - use array to distinguish between uncached and cached, but null. */ 165 private String[] m_formatterKey; 166 167 /** Cache for the wrapped element parent. */ 168 private CmsContainerElementWrapper m_parent; 169 170 /** Cache for the wrapped element type name. */ 171 private String m_resourceTypeName; 172 173 /** Cache for the wrapped element settings. */ 174 private Map<String, CmsJspElementSettingValueWrapper> m_wrappedSettings; 175 176 /** 177 * Constructor.<p> 178 * 179 * @param element the element to wrap 180 */ 181 protected CmsContainerElementWrapper(Supplier<CmsContainerElementBean> elementProvider) { 182 183 m_elementProvider = elementProvider; 184 185 } 186 187 /** 188 * @see org.opencms.xml.containerpage.CmsContainerElementBean#clone() 189 */ 190 @Override 191 public CmsContainerElementBean clone() { 192 193 return m_elementProvider.get().clone(); 194 } 195 196 /** 197 * @see org.opencms.xml.containerpage.CmsContainerElementBean#editorHash() 198 */ 199 @Override 200 public String editorHash() { 201 202 return m_elementProvider.get().editorHash(); 203 } 204 205 /** 206 * @see org.opencms.xml.containerpage.CmsContainerElementBean#equals(java.lang.Object) 207 */ 208 @Override 209 public boolean equals(Object obj) { 210 211 return m_elementProvider.get().equals(obj); 212 } 213 214 /** 215 * @see org.opencms.xml.containerpage.CmsContainerElementBean#getFormatterId() 216 */ 217 @Override 218 public CmsUUID getFormatterId() { 219 220 return m_elementProvider.get().getFormatterId(); 221 } 222 223 /** 224 * Returns the formatter key, if possible, otherwise the formatter configuration id, or null if nothing at all can be found. 225 * 226 * @return the formatter key 227 */ 228 public String getFormatterKey() { 229 230 if (m_formatterKey == null) { 231 String key = null; 232 I_CmsFormatterBean formatter = getElementFormatter(m_elementProvider.get()); 233 if (formatter != null) { 234 key = formatter.getKeyOrId(); 235 } 236 m_formatterKey = new String[] {key}; 237 } 238 return m_formatterKey[0]; 239 } 240 241 /** 242 * @see org.opencms.xml.containerpage.CmsContainerElementBean#getId() 243 */ 244 @Override 245 public CmsUUID getId() { 246 247 return m_elementProvider.get().getId(); 248 } 249 250 /** 251 * @see org.opencms.xml.containerpage.CmsContainerElementBean#getIndividualSettings() 252 */ 253 @Override 254 public Map<String, String> getIndividualSettings() { 255 256 return m_elementProvider.get().getIndividualSettings(); 257 } 258 259 /** 260 * @see org.opencms.xml.containerpage.CmsContainerElementBean#getInheritanceInfo() 261 */ 262 @Override 263 public CmsInheritanceInfo getInheritanceInfo() { 264 265 return m_elementProvider.get().getInheritanceInfo(); 266 } 267 268 /** 269 * @see org.opencms.xml.containerpage.CmsContainerElementBean#getInstanceId() 270 */ 271 @Override 272 public String getInstanceId() { 273 274 return m_elementProvider.get().getInstanceId(); 275 } 276 277 /** 278 * Returns the parent element if present.<p> 279 * 280 * @return the parent element or <code>null</code> if not available 281 */ 282 public CmsContainerElementWrapper getParent() { 283 284 if (m_parent == null) { 285 CmsContainerElementBean parent = getParentElement(m_elementProvider.get()); 286 m_parent = (parent != null) 287 ? new CmsContainerElementWrapper(() -> getParentElement(m_elementProvider.get())) 288 : null; 289 } 290 return m_parent; 291 } 292 293 /** 294 * @see org.opencms.xml.containerpage.CmsContainerElementBean#getResource() 295 */ 296 @Override 297 public CmsResource getResource() { 298 299 return m_elementProvider.get().getResource(); 300 } 301 302 /** 303 * Returns the resource type name of the element resource.<p> 304 * 305 * @return the resource type name 306 */ 307 public String getResourceTypeName() { 308 309 if (m_resourceTypeName == null) { 310 m_resourceTypeName = ""; 311 try { 312 m_resourceTypeName = OpenCms.getResourceManager().getResourceType( 313 m_elementProvider.get().getResource()).getTypeName(); 314 } catch (Exception e) { 315 CmsJspStandardContextBean.LOG.error(e.getLocalizedMessage(), e); 316 } 317 } 318 return m_resourceTypeName; 319 } 320 321 /** 322 * Returns a lazy initialized setting map.<p> 323 * 324 * The values returned in the map are instances of {@link A_CmsJspValueWrapper}. 325 * 326 * @return the wrapped settings 327 */ 328 public Map<String, CmsJspElementSettingValueWrapper> getSetting() { 329 330 if ((m_wrappedSettings == null) || (m_wrappedElement != m_elementProvider.get())) { 331 m_wrappedElement = m_elementProvider.get(); 332 m_wrappedSettings = CmsCollectionsGenericWrapper.createLazyMap( 333 new SettingsTransformer(m_wrappedElement)); 334 } 335 return m_wrappedSettings; 336 } 337 338 /** 339 * @see org.opencms.xml.containerpage.CmsContainerElementBean#getSettings() 340 */ 341 @Override 342 public Map<String, String> getSettings() { 343 344 return m_elementProvider.get().getSettings(); 345 } 346 347 /** 348 * @see org.opencms.xml.containerpage.CmsContainerElementBean#getSitePath() 349 */ 350 @Override 351 public String getSitePath() { 352 353 return m_elementProvider.get().getSitePath(); 354 } 355 356 /** 357 * @see org.opencms.xml.containerpage.CmsContainerElementBean#hashCode() 358 */ 359 @Override 360 public int hashCode() { 361 362 return m_elementProvider.get().hashCode(); 363 } 364 365 /** 366 * @see org.opencms.xml.containerpage.CmsContainerElementBean#initResource(org.opencms.file.CmsObject) 367 */ 368 @Override 369 public void initResource(CmsObject cms) throws CmsException { 370 371 m_elementProvider.get().initResource(cms); 372 } 373 374 /** 375 * @see org.opencms.xml.containerpage.CmsContainerElementBean#initSettings(org.opencms.file.CmsObject, org.opencms.ade.configuration.CmsADEConfigData, org.opencms.xml.containerpage.I_CmsFormatterBean, java.util.Locale, javax.servlet.ServletRequest, java.util.Map) 376 */ 377 @Override 378 public void initSettings( 379 CmsObject cms, 380 CmsADEConfigData config, 381 I_CmsFormatterBean formatterBean, 382 Locale locale, 383 ServletRequest request, 384 Map<String, String> settingPresets) { 385 386 m_elementProvider.get().initSettings(cms, config, formatterBean, locale, request, settingPresets); 387 } 388 389 /** 390 * @see org.opencms.xml.containerpage.CmsContainerElementBean#isCreateNew() 391 */ 392 @Override 393 public boolean isCreateNew() { 394 395 return m_elementProvider.get().isCreateNew(); 396 } 397 398 /** 399 * @see org.opencms.xml.containerpage.CmsContainerElementBean#isGroupContainer(org.opencms.file.CmsObject) 400 */ 401 @Override 402 public boolean isGroupContainer(CmsObject cms) throws CmsException { 403 404 return m_elementProvider.get().isGroupContainer(cms); 405 } 406 407 /** 408 * @see org.opencms.xml.containerpage.CmsContainerElementBean#isHistoryContent() 409 */ 410 @Override 411 public boolean isHistoryContent() { 412 413 return m_elementProvider.get().isHistoryContent(); 414 } 415 416 /** 417 * @see org.opencms.xml.containerpage.CmsContainerElementBean#isInheritedContainer(org.opencms.file.CmsObject) 418 */ 419 @Override 420 public boolean isInheritedContainer(CmsObject cms) throws CmsException { 421 422 return m_elementProvider.get().isInheritedContainer(cms); 423 } 424 425 /** 426 * @see org.opencms.xml.containerpage.CmsContainerElementBean#isInMemoryOnly() 427 */ 428 @Override 429 public boolean isInMemoryOnly() { 430 431 return m_elementProvider.get().isInMemoryOnly(); 432 } 433 434 /** 435 * @see org.opencms.xml.containerpage.CmsContainerElementBean#isReleasedAndNotExpired() 436 */ 437 @Override 438 public boolean isReleasedAndNotExpired() { 439 440 return m_elementProvider.get().isReleasedAndNotExpired(); 441 } 442 443 /** 444 * @see org.opencms.xml.containerpage.CmsContainerElementBean#isTemporaryContent() 445 */ 446 @Override 447 public boolean isTemporaryContent() { 448 449 return m_elementProvider.get().isTemporaryContent(); 450 } 451 452 /** 453 * @see org.opencms.xml.containerpage.CmsContainerElementBean#setFormatterId(org.opencms.util.CmsUUID) 454 */ 455 @Override 456 public void setFormatterId(CmsUUID formatterId) { 457 458 m_elementProvider.get().setFormatterId(formatterId); 459 } 460 461 /** 462 * @see org.opencms.xml.containerpage.CmsContainerElementBean#setHistoryFile(org.opencms.file.CmsFile) 463 */ 464 @Override 465 public void setHistoryFile(CmsFile file) { 466 467 m_elementProvider.get().setHistoryFile(file); 468 } 469 470 /** 471 * @see org.opencms.xml.containerpage.CmsContainerElementBean#setInheritanceInfo(org.opencms.ade.containerpage.shared.CmsInheritanceInfo) 472 */ 473 @Override 474 public void setInheritanceInfo(CmsInheritanceInfo inheritanceInfo) { 475 476 m_elementProvider.get().setInheritanceInfo(inheritanceInfo); 477 } 478 479 /** 480 * @see org.opencms.xml.containerpage.CmsContainerElementBean#setTemporaryFile(org.opencms.file.CmsFile) 481 */ 482 @Override 483 public void setTemporaryFile(CmsFile elementFile) { 484 485 m_elementProvider.get().setTemporaryFile(elementFile); 486 } 487 488 /** 489 * @see org.opencms.xml.containerpage.CmsContainerElementBean#toString() 490 */ 491 @Override 492 public String toString() { 493 494 return m_elementProvider.get().toString(); 495 } 496 } 497 498 /** 499 * Provides a lazy initialized Map that provides the detail page link as a value when given the name of a 500 * (named) dynamic function or resource type as a key.<p> 501 */ 502 public class CmsDetailLookupTransformer implements Transformer { 503 504 /** The selected prefix. */ 505 private String m_prefix; 506 507 /** 508 * Constructor with a prefix.<p> 509 * 510 * The prefix is used to distinguish between type detail pages and function detail pages.<p> 511 * 512 * @param prefix the prefix to use 513 */ 514 public CmsDetailLookupTransformer(String prefix) { 515 516 m_prefix = prefix; 517 } 518 519 /** 520 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 521 */ 522 @Override 523 public Object transform(Object input) { 524 525 String prefix = m_prefix; 526 CmsObject cms = m_cms; 527 String inputStr = String.valueOf(input); 528 529 return getFunctionDetailLink(cms, prefix, inputStr, false); 530 } 531 } 532 533 /** 534 * The element setting transformer.<p> 535 */ 536 public class SettingsTransformer implements Transformer { 537 538 /** The element formatter config. */ 539 private I_CmsFormatterBean m_formatter; 540 541 /** The configured formatter settings. */ 542 private Map<String, CmsXmlContentProperty> m_formatterSettingsConfig; 543 544 /** The element. */ 545 private CmsContainerElementBean m_transformElement; 546 547 /** 548 * Constructor.<p> 549 * 550 * @param element the element 551 */ 552 SettingsTransformer(CmsContainerElementBean element) { 553 554 m_transformElement = element; 555 m_formatter = getElementFormatter(element); 556 } 557 558 /** 559 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 560 */ 561 @Override 562 public Object transform(Object settingName) { 563 564 boolean exists; 565 if (m_formatter != null) { 566 if (m_formatterSettingsConfig == null) { 567 m_formatterSettingsConfig = OpenCms.getADEManager().getFormatterSettings( 568 m_cms, 569 m_config, 570 m_formatter, 571 m_transformElement.getResource(), 572 getLocale(), 573 m_request); 574 } 575 576 // the first condition is used to catch shared settings of nested formatters, 577 // the second condition is used to catch settings with visibility parentShared 578 // (just because you can't edit them on the child element doesn't mean they don't exist!) 579 exists = (m_formatterSettingsConfig.get(settingName) != null) 580 || m_formatter.getSettings(m_config).containsKey(settingName); 581 } else { 582 exists = m_transformElement.getSettings().get(settingName) != null; 583 } 584 return new CmsJspElementSettingValueWrapper( 585 m_cms, 586 m_transformElement.getSettings().get(settingName), 587 exists); 588 } 589 } 590 591 /** 592 * Bean containing a template name and URI.<p> 593 */ 594 public static class TemplateBean { 595 596 /** True if the template context was manually selected. */ 597 private boolean m_forced; 598 599 /** The template name. */ 600 private String m_name; 601 602 /** The template resource. */ 603 private CmsResource m_resource; 604 605 /** The template uri, if no resource is set. */ 606 private String m_uri; 607 608 /** 609 * Creates a new instance.<p> 610 * 611 * @param name the template name 612 * @param resource the template resource 613 */ 614 public TemplateBean(String name, CmsResource resource) { 615 616 m_resource = resource; 617 m_name = name; 618 } 619 620 /** 621 * Creates a new instance with an URI instead of a resoure.<p> 622 * 623 * @param name the template name 624 * @param uri the template uri 625 */ 626 public TemplateBean(String name, String uri) { 627 628 m_name = name; 629 m_uri = uri; 630 } 631 632 /** 633 * Gets the template name.<p> 634 * 635 * @return the template name 636 */ 637 public String getName() { 638 639 return m_name; 640 } 641 642 /** 643 * Gets the template resource.<p> 644 * 645 * @return the template resource 646 */ 647 public CmsResource getResource() { 648 649 return m_resource; 650 } 651 652 /** 653 * Gets the template uri.<p> 654 * 655 * @return the template URI. 656 */ 657 public String getUri() { 658 659 if (m_resource != null) { 660 return m_resource.getRootPath(); 661 } else { 662 return m_uri; 663 } 664 } 665 666 /** 667 * Returns true if the template context was manually selected.<p> 668 * 669 * @return true if the template context was manually selected 670 */ 671 public boolean isForced() { 672 673 return m_forced; 674 } 675 676 /** 677 * Sets the 'forced' flag to a new value.<p> 678 * 679 * @param forced the new value 680 */ 681 public void setForced(boolean forced) { 682 683 m_forced = forced; 684 } 685 686 } 687 688 /** 689 * The meta mappings transformer.<p> 690 */ 691 class MetaLookupTranformer implements Transformer { 692 693 /** 694 * @see org.apache.commons.collections.Transformer#transform(java.lang.Object) 695 */ 696 public Object transform(Object arg0) { 697 698 String result = null; 699 if ((m_metaMappings != null) && m_metaMappings.containsKey(arg0)) { 700 MetaMapping mapping = m_metaMappings.get(arg0); 701 CmsGalleryNameMacroResolver resolver = null; 702 try { 703 CmsResourceFilter filter = getIsEditMode() 704 ? CmsResourceFilter.IGNORE_EXPIRATION 705 : CmsResourceFilter.DEFAULT; 706 CmsResource res = m_cms.readResource(mapping.m_contentId, filter); 707 CmsXmlContent content = CmsXmlContentFactory.unmarshal(m_cms, res, m_request); 708 resolver = new CmsGalleryNameMacroResolver(m_cms, content, getLocale()); 709 if (content.hasLocale(getLocale())) { 710 I_CmsXmlContentValue val = content.getValue(mapping.m_elementXPath, getLocale()); 711 if (val != null) { 712 result = val.getStringValue(m_cms); 713 } 714 } 715 716 } catch (CmsException e) { 717 LOG.error(e.getLocalizedMessage(), e); 718 } 719 if (result == null) { 720 result = mapping.m_defaultValue; 721 } 722 if ((resolver != null) && (result != null)) { 723 result = resolver.resolveMacros(result); 724 } 725 } 726 return result; 727 } 728 729 } 730 731 /** The meta mapping data. */ 732 class MetaMapping { 733 734 /** The mapping content structure id. */ 735 CmsUUID m_contentId; 736 737 /** The default value. */ 738 String m_defaultValue; 739 740 /** The mapping value xpath. */ 741 String m_elementXPath; 742 743 /** The mapping key. */ 744 String m_key; 745 746 /** The mapping order. */ 747 int m_order; 748 } 749 750 /** The attribute name of the cms object.*/ 751 public static final String ATTRIBUTE_CMS_OBJECT = "__cmsObject"; 752 753 /** The attribute name of the standard JSP context bean. */ 754 public static final String ATTRIBUTE_NAME = "cms"; 755 756 /** The logger instance for this class. */ 757 protected static final Log LOG = CmsLog.getLog(CmsJspStandardContextBean.class); 758 759 /** Used for color contrast calculations. */ 760 private static final CmsColorContrastCalculator m_color = new CmsColorContrastCalculator(); 761 762 /** OpenCms user context. */ 763 protected CmsObject m_cms; 764 765 /** The sitemap configuration. */ 766 protected CmsADEConfigData m_config; 767 768 /** The meta mapping configuration. */ 769 Map<String, MetaMapping> m_metaMappings; 770 771 /** The current request. */ 772 ServletRequest m_request; 773 774 /** Lazily initialized map from a category path to all sub-categories of that category. */ 775 private Map<String, CmsJspCategoryAccessBean> m_allSubCategories; 776 777 /** Lazily initialized nested map for reading either attributes or properties (first key: file name, second key: attribute / property name). */ 778 private Map<String, Map<String, CmsJspObjectValueWrapper>> m_attributesOrProperties; 779 780 /** Lazily initialized map from a category path to the path's category object. */ 781 private Map<String, CmsCategory> m_categories; 782 783 /** The container the currently rendered element is part of. */ 784 private CmsContainerBean m_container; 785 786 /** The current detail content resource if available. */ 787 private CmsResource m_detailContentResource; 788 789 /** The detail function page. */ 790 private CmsResource m_detailFunctionPage; 791 792 /** The detail only page references containers that are only displayed in detail view. */ 793 private CmsContainerPageBean m_detailOnlyPage; 794 795 /** Flag to indicate if element was just edited. */ 796 private boolean m_edited; 797 798 /** The currently rendered element. */ 799 private CmsContainerElementBean m_element; 800 801 /** The elements of the current page. */ 802 private Map<String, CmsContainerElementBean> m_elementInstances; 803 804 /** Flag to force edit mode to be disabled. */ 805 private boolean m_forceDisableEditMode; 806 807 /** The lazy initialized map which allows access to the dynamic function beans. */ 808 private Map<String, CmsDynamicFunctionBeanWrapper> m_function; 809 810 /** The lazy initialized map for the function detail pages. */ 811 private Map<String, String> m_functionDetailPage; 812 813 /** The lazy initialized map for the function detail pages. */ 814 private Map<String, String> m_functionDetailPageExact; 815 816 /** Indicates if in drag mode. */ 817 private boolean m_isDragMode; 818 819 /** Stores the edit mode info. */ 820 private Boolean m_isEditMode; 821 822 /** Lazily initialized map from the locale to the localized title property. */ 823 private Map<String, String> m_localeTitles; 824 825 /** The currently displayed container page. */ 826 private CmsContainerPageBean m_page; 827 828 /** The current container page resource, lazy initialized. */ 829 private CmsJspResourceWrapper m_pageResource; 830 831 /** The parent containers to the given element instance ids. */ 832 private Map<String, CmsContainerBean> m_parentContainers; 833 834 /** Lazily initialized map from a category path to all categories on that path. */ 835 private Map<String, List<CmsCategory>> m_pathCategories; 836 837 /** Lazily initialized map from the root path of a resource to all categories assigned to the resource. */ 838 private Map<String, CmsJspCategoryAccessBean> m_resourceCategories; 839 840 /** Map from root paths to site relative paths. */ 841 private Map<String, String> m_sitePaths; 842 843 /** The template plugins. */ 844 private Map<String, List<CmsTemplatePluginWrapper>> m_templatePlugins; 845 846 /** The lazy initialized map for the detail pages. */ 847 private Map<String, String> m_typeDetailPage; 848 849 /** The VFS content access bean. */ 850 private CmsJspVfsAccessBean m_vfsBean; 851 852 /** 853 * Creates an empty instance.<p> 854 */ 855 private CmsJspStandardContextBean() { 856 857 } 858 859 /** 860 * Creates a new standard JSP context bean. 861 * 862 * @param req the current servlet request 863 */ 864 private CmsJspStandardContextBean(ServletRequest req) { 865 866 this(); 867 CmsFlexController controller = CmsFlexController.getController(req); 868 m_request = req; 869 CmsObject cms; 870 if (controller != null) { 871 cms = controller.getCmsObject(); 872 } else { 873 cms = (CmsObject)req.getAttribute(ATTRIBUTE_CMS_OBJECT); 874 } 875 if (cms == null) { 876 // cms object unavailable - this request was not initialized properly 877 throw new CmsRuntimeException( 878 Messages.get().container(Messages.ERR_MISSING_CMS_CONTROLLER_1, CmsJspBean.class.getName())); 879 } 880 updateCmsObject(cms); 881 m_detailContentResource = CmsDetailPageResourceHandler.getDetailResource(req); 882 m_detailFunctionPage = CmsDetailPageResourceHandler.getDetailFunctionPage(req); 883 } 884 885 /** 886 * Gets the link to a function detail page. 887 * 888 * @param cms the CMS context 889 * @param prefix the function detail prefix 890 * @param functionName the function name 891 * @param fullLink true if links should be generated with server prefix 892 * 893 * @return the link 894 */ 895 public static String getFunctionDetailLink(CmsObject cms, String prefix, String functionName, boolean fullLink) { 896 897 String type = prefix + functionName; 898 899 CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration( 900 cms, 901 cms.addSiteRoot(cms.getRequestContext().getUri())); 902 List<CmsDetailPageInfo> detailPages = config.getDetailPagesForType(type); 903 CmsDetailPageInfo detailPage = null; 904 if ((detailPages == null) || (detailPages.size() == 0)) { 905 detailPage = config.getDefaultDetailPage(); 906 } else { 907 detailPage = detailPages.get(0); 908 } 909 if (detailPage == null) { 910 return "[No detail page configured for type =" + type + "=]"; 911 } 912 913 CmsUUID id = detailPage.getId(); 914 try { 915 CmsResource r = cms.readResource(id); 916 boolean originalForceAbsoluteLinks = cms.getRequestContext().isForceAbsoluteLinks(); 917 try { 918 cms.getRequestContext().setForceAbsoluteLinks(fullLink || originalForceAbsoluteLinks); 919 String link = OpenCms.getLinkManager().substituteLink(cms, r); 920 return link; 921 } finally { 922 cms.getRequestContext().setForceAbsoluteLinks(originalForceAbsoluteLinks); 923 } 924 } catch (CmsException e) { 925 LOG.warn(e.getLocalizedMessage(), e); 926 return "[Error reading detail page for type =" + type + "=]"; 927 } 928 } 929 930 /** 931 * Gets the link to a function detail page. 932 * 933 * <p>This just returns null if no function detail page is defined, it does not use the default detail page as a fallback. 934 * 935 * @param cms the CMS context 936 * @param functionName the function name 937 * 938 * @return the link 939 */ 940 public static String getFunctionDetailLinkExact(CmsObject cms, String functionName) { 941 942 String type = CmsDetailPageInfo.FUNCTION_PREFIX + functionName; 943 944 CmsADEConfigData config = OpenCms.getADEManager().lookupConfigurationWithCache( 945 cms, 946 cms.addSiteRoot(cms.getRequestContext().getUri())); 947 List<CmsDetailPageInfo> detailPages = config.getDetailPagesForType(type); 948 949 CmsDetailPageInfo detailPage = null; 950 if ((detailPages == null) || (detailPages.size() == 0)) { 951 return null; 952 } 953 detailPage = detailPages.get(0); 954 if (detailPage.isDefaultDetailPage()) { 955 return null; 956 } 957 958 CmsUUID id = detailPage.getId(); 959 try { 960 CmsResource r = cms.readResource(id); 961 String link = OpenCms.getLinkManager().substituteLink(cms, r); 962 return link; 963 } catch (CmsException e) { 964 LOG.warn(e.getLocalizedMessage(), e); 965 return null; 966 } 967 968 } 969 970 /** 971 * Creates a new instance of the standard JSP context bean.<p> 972 * 973 * To prevent multiple creations of the bean during a request, the OpenCms request context 974 * attributes are used to cache the created VFS access utility bean.<p> 975 * 976 * @param req the current servlet request 977 * 978 * @return a new instance of the standard JSP context bean 979 */ 980 public static CmsJspStandardContextBean getInstance(ServletRequest req) { 981 982 Object attribute = req.getAttribute(ATTRIBUTE_NAME); 983 CmsJspStandardContextBean result; 984 if ((attribute != null) && (attribute instanceof CmsJspStandardContextBean)) { 985 result = (CmsJspStandardContextBean)attribute; 986 } else { 987 result = new CmsJspStandardContextBean(req); 988 req.setAttribute(ATTRIBUTE_NAME, result); 989 } 990 return result; 991 } 992 993 /** 994 * Returns a copy of this JSP context bean.<p> 995 * 996 * @return a copy of this JSP context bean 997 */ 998 public CmsJspStandardContextBean createCopy() { 999 1000 CmsJspStandardContextBean result = new CmsJspStandardContextBean(); 1001 result.m_container = m_container; 1002 if (m_detailContentResource != null) { 1003 result.m_detailContentResource = m_detailContentResource.getCopy(); 1004 } 1005 result.m_element = m_element; 1006 result.m_forceDisableEditMode = m_forceDisableEditMode; 1007 result.setPage(m_page); 1008 return result; 1009 } 1010 1011 /** 1012 * Uses the default text encryption method to decrypt an encrypted string. 1013 * 1014 * @param text the encrypted stirng 1015 * @return the decrypted string 1016 */ 1017 public String decrypt(String text) { 1018 1019 try { 1020 return OpenCms.getTextEncryptions().get("default").decrypt(text); 1021 } catch (Exception e) { 1022 return null; 1023 } 1024 1025 } 1026 1027 /** 1028 * Returns a caching hash specific to the element, it's properties and the current container width.<p> 1029 * 1030 * @return the caching hash 1031 */ 1032 public String elementCachingHash() { 1033 1034 String result = ""; 1035 if (m_element != null) { 1036 result = m_element.editorHash(); 1037 if (m_container != null) { 1038 result += "w:" 1039 + m_container.getWidth() 1040 + "cName:" 1041 + m_container.getName() 1042 + "cType:" 1043 + m_container.getType(); 1044 } 1045 } 1046 return result; 1047 } 1048 1049 /** 1050 * Uses the default text encryption to encrypt an input text. 1051 * 1052 * @param text the input text 1053 * @return the encrypted text 1054 */ 1055 public String encrypt(String text) { 1056 1057 try { 1058 return OpenCms.getTextEncryptions().get("default").encrypt(text); 1059 } catch (Exception e) { 1060 return null; 1061 } 1062 } 1063 1064 /** 1065 * Checks if the resource with the given path exists. 1066 * 1067 * @param path a path 1068 * @return true if the resource exists 1069 */ 1070 public boolean exists(String path) { 1071 1072 Boolean exists = getVfs().getExists().get(path); 1073 return exists != null ? exists.booleanValue() : false; 1074 1075 } 1076 1077 /** 1078 * Returns the locales available for the currently requested URI. 1079 * 1080 * @return the locales available for the currently requested URI. 1081 */ 1082 public List<Locale> getAvailableLocales() { 1083 1084 return OpenCms.getLocaleManager().getAvailableLocales(m_cms, getRequestContext().getUri()); 1085 } 1086 1087 /** 1088 * Helper for easy instantiation and initialization of custom context beans that returns 1089 * an instance of the class specified via <code>className</code>, with the current context already set. 1090 * 1091 * @param className name of the class to instantiate. Must be a subclass of {@link A_CmsJspCustomContextBean}. 1092 * @return an instance of the provided class with the current context already set. 1093 */ 1094 public Object getBean(String className) { 1095 1096 try { 1097 Class<?> clazz = Class.forName(className); 1098 if (A_CmsJspCustomContextBean.class.isAssignableFrom(clazz)) { 1099 Constructor<?> constructor = clazz.getConstructor(); 1100 Object instance = constructor.newInstance(); 1101 Method setContextMethod = clazz.getMethod("setContext", CmsJspStandardContextBean.class); 1102 setContextMethod.invoke(instance, this); 1103 return instance; 1104 } else { 1105 throw new Exception(); 1106 } 1107 } catch (Exception e) { 1108 LOG.error(Messages.get().container(Messages.ERR_NO_CUSTOM_BEAN_1, className)); 1109 } 1110 return null; 1111 1112 } 1113 1114 /** 1115 * Finds the folder to use for binary uploads, based on the list configuration given as an argument or 1116 * the current sitemap configuration. 1117 * 1118 * @param content the list configuration content 1119 * 1120 * @return the binary upload folder 1121 */ 1122 public String getBinaryUploadFolder(CmsJspContentAccessBean content) { 1123 1124 String keyToFind = CmsADEConfigData.ATTR_BINARY_UPLOAD_TARGET; 1125 String baseValue = null; 1126 if (content != null) { 1127 for (CmsJspContentAccessValueWrapper wrapper : content.getValueList().get( 1128 CmsConfigParserUtils.N_PARAMETER)) { 1129 String paramKey = wrapper.getValue().get(CmsConfigParserUtils.N_KEY).getToString(); 1130 String paramValue = wrapper.getValue().get(CmsConfigParserUtils.N_VALUE).getToString(); 1131 if (paramKey.equals(keyToFind)) { 1132 LOG.debug("Found upload folder in configuration: " + paramValue); 1133 baseValue = paramValue; 1134 break; 1135 } 1136 } 1137 1138 if (baseValue == null) { 1139 List<CmsJspContentAccessValueWrapper> folderEntries = content.getValueList().get( 1140 CmsConfigParserUtils.N_SEARCH_FOLDER); 1141 if (folderEntries.size() == 1) { 1142 CmsResource resource = folderEntries.get(0).getToResource(); 1143 List<String> galleryTypes = Arrays.asList( 1144 CmsAjaxDownloadGallery.GALLERYTYPE_NAME, 1145 CmsAjaxImageGallery.GALLERYTYPE_NAME); 1146 if ((resource != null) && (null != findAncestor(m_cms, resource, (ancestor) -> { 1147 return galleryTypes.stream().anyMatch( 1148 type -> OpenCms.getResourceManager().matchResourceType(type, ancestor.getTypeId())); 1149 }))) { 1150 baseValue = m_cms.getSitePath(resource); 1151 LOG.debug( 1152 "Using single download gallery from search folder configuration as upload folder: " 1153 + baseValue); 1154 1155 } 1156 } 1157 } 1158 } 1159 1160 if (baseValue == null) { 1161 baseValue = m_config.getAttribute(keyToFind, null); 1162 if (baseValue != null) { 1163 LOG.debug("Found upload folder in sitemap configuration: " + baseValue); 1164 } 1165 } 1166 1167 CmsMacroResolver resolver = new CmsMacroResolver(); 1168 resolver.setCmsObject(getCmsObject()); 1169 resolver.addMacro("subsitepath", CmsFileUtil.removeTrailingSeparator(getSubSitePath())); 1170 resolver.addMacro("sitepath", "/"); 1171 1172 // if baseValue is still null, then resolveMacros will just return null 1173 String result = resolver.resolveMacros(baseValue); 1174 1175 LOG.debug("Final value for upload folder : " + result); 1176 return result; 1177 } 1178 1179 /** 1180 * Generates a link to the bundle editor to edit the provided message key. 1181 * The back link for the editor is the current uri. 1182 * 1183 * If the bundle for the key could not be found, <code>null</code> is returned. 1184 * 1185 * @param messageKey the message key to open the bundle editor for. 1186 * 1187 * @return a link to the bundle editor for editing the provided key, or <code>null</code> if the bundle for the key could not be found. 1188 */ 1189 public String getBundleEditorLink(String messageKey) { 1190 1191 return getBundleEditorLink(messageKey, null); 1192 } 1193 1194 /** 1195 * Generates a link to the bundle editor to edit the provided message key. 1196 * The back link for the editor is the current uri with the provided backLinkAnchor added as anchor.. 1197 * 1198 * If the bundle for the key could not be found, <code>null</code> is returned. 1199 * 1200 * @param messageKey the message key to open the bundle editor for. 1201 * @param backLinkAnchor the anchor id to add to the backlink to the page. If <code>null</code> no anchor is added to the backlink. 1202 * 1203 * @return a link to the bundle editor for editing the provided key, or <code>null</code> if the bundle for the key could not be found. 1204 */ 1205 public String getBundleEditorLink(String messageKey, String backLinkAnchor) { 1206 1207 return getBundleEditorLink(messageKey, backLinkAnchor, null); 1208 } 1209 1210 /** 1211 * Generates a link to the bundle editor to edit the provided message key. 1212 * The back link for the editor is the current uri with the provided backLinkAnchor added as anchor. 1213 * 1214 * If the bundle resource for the key could not be found, <code>null</code> is returned. 1215 * 1216 * @param messageKey the message key to open the bundle editor for. 1217 * @param backLinkAnchor the anchor id to add to the backlink to the page. If <code>null</code> no anchor is added to the backlink. 1218 * @param backLinkParams request parameters to add to the backlink without leading '?', e.g. "param1=a¶m2=b". 1219 * 1220 * @return a link to the bundle editor for editing the provided key, or <code>null</code> if the bundle for the key could not be found. 1221 */ 1222 public String getBundleEditorLink(String messageKey, String backLinkAnchor, String backLinkParams) { 1223 1224 return getBundleEditorLink(messageKey, backLinkAnchor, backLinkParams, null, null); 1225 } 1226 1227 /** 1228 * Generates a link to the bundle editor to edit the provided message key. 1229 * The back link for the editor is the current uri with the provided backLinkAnchor added as anchor. 1230 * 1231 * If the bundle resource for the key could not be found, <code>null</code> is returned. 1232 * 1233 * @param messageKey the message key to open the bundle editor for. 1234 * @param backLinkAnchor the anchor id to add to the backlink to the page. If <code>null</code> no anchor is added to the backlink. 1235 * @param backLinkParams request parameters to add to the backlink without leading '?', e.g. "param1=a¶m2=b". 1236 * @param bundleFilters substrings of names of bundles to be preferred when multiple bundles contain the key. 1237 * 1238 * @return a link to the bundle editor for editing the provided key, or <code>null</code> if the bundle for the key could not be found. 1239 */ 1240 public String getBundleEditorLink( 1241 String messageKey, 1242 String backLinkAnchor, 1243 String backLinkParams, 1244 List<String> bundleFilters) { 1245 1246 return getBundleEditorLink(messageKey, backLinkAnchor, backLinkParams, null, bundleFilters); 1247 1248 } 1249 1250 /** 1251 * Generates a link to the bundle editor to edit the provided message key. 1252 * The back link for the editor is the current uri with the provided backLinkAnchor added as anchor. 1253 * 1254 * If the bundle resource for the key could not be found, <code>null</code> is returned. 1255 * 1256 * @param messageKey the message key to open the bundle editor for. 1257 * @param backLinkAnchor the anchor id to add to the backlink to the page. If <code>null</code> no anchor is added to the backlink. 1258 * @param backLinkParams request parameters to add to the backlink without leading '?', e.g. "param1=a¶m2=b". 1259 * @param bundleName the name of the bundle to search the key in. If <code>null</code> the bundle is detected automatically. 1260 * 1261 * @return a link to the bundle editor for editing the provided key, or <code>null</code> if the bundle for the key could not be found. 1262 */ 1263 public String getBundleEditorLinkForBundle( 1264 String messageKey, 1265 String backLinkAnchor, 1266 String backLinkParams, 1267 String bundleName) { 1268 1269 return getBundleEditorLink(messageKey, backLinkAnchor, backLinkParams, bundleName, null); 1270 1271 } 1272 1273 /** 1274 * Gets the root path for the VFS-based message bundle containing the given message key. 1275 * 1276 * <p>If no VFS-based message bundle contains the given key, null is returned. If multiple message bundles contain it, 1277 * the name filters are applied in the given order until at least one bundle matches a filter. 1278 * If multiple bundles match, one of them is arbitrarily chosen (but a warning is logged). 1279 * If no bundle matches, an arbitrary bundle is chosen (but also a warning is logged). 1280 * 1281 * <p>Note: This uses the online (published) state of message bundles, so if you have unpublished bundle changes, they will not be reflected in 1282 * the result. 1283 * 1284 * @param messageKey the message key 1285 * @param bundleFilters substrings of names of bundles to be preferred when multiple bundles contain the key. 1286 * @return the root path of the bundle containing the message key 1287 */ 1288 public String getBundleRootPath(String messageKey, List<String> bundleFilters) { 1289 1290 CmsObject cms = getCmsObject(); 1291 try { 1292 CmsMessageToBundleIndex bundleIndex = null; 1293 OpenCmsServlet.RequestCache context = OpenCmsServlet.getRequestCache(); 1294 if (context != null) { 1295 bundleIndex = (CmsMessageToBundleIndex)context.getAttribute( 1296 CmsMessageToBundleIndex.class.getName() + "_" + cms.getRequestContext().getLocale(), 1297 k -> { 1298 try { 1299 CmsMessageToBundleIndex result = CmsMessageToBundleIndex.read(getCmsObject()); 1300 return result; 1301 } catch (Exception e) { 1302 throw new RuntimeException(e); 1303 } 1304 }); 1305 1306 } else { 1307 bundleIndex = CmsMessageToBundleIndex.read(getCmsObject()); 1308 } 1309 Collection<String> bundles = bundleIndex.getBundlesPathForKey(messageKey); 1310 switch (bundles.size()) { 1311 case 0: 1312 return null; 1313 case 1: 1314 return bundles.iterator().next(); 1315 default: 1316 if (!((null == bundleFilters) || bundleFilters.isEmpty())) { 1317 for (String filter : bundleFilters) { 1318 Set<String> matchingBundles = new HashSet<>(bundles.size()); 1319 for (String bundle : bundles) { 1320 if (bundle.contains(filter)) { 1321 matchingBundles.add(bundle); 1322 } 1323 } 1324 if (matchingBundles.size() > 0) { 1325 if (matchingBundles.size() > 1) { 1326 LOG.warn( 1327 "Ambiguous message bundle for key " 1328 + messageKey 1329 + " and filter " 1330 + filter 1331 + ":" 1332 + matchingBundles); 1333 } 1334 return matchingBundles.iterator().next(); 1335 } 1336 } 1337 } 1338 LOG.warn("Ambiguous message bundle for key " + messageKey + ":" + bundles); 1339 return bundles.iterator().next(); 1340 } 1341 } catch (Exception e) { 1342 LOG.error(e.getLocalizedMessage(), e); 1343 return null; 1344 } 1345 } 1346 1347 /** 1348 * Gets the bean to use for color calculations. 1349 * 1350 * @return the bean for color calculation 1351 */ 1352 public CmsColorContrastCalculator getColor() { 1353 1354 return m_color; 1355 } 1356 1357 /** 1358 * Returns the container the currently rendered element is part of.<p> 1359 * 1360 * @return the container the currently rendered element is part of 1361 */ 1362 public CmsContainerBean getContainer() { 1363 1364 return m_container; 1365 } 1366 1367 /** 1368 * Gets information about a given container type. 1369 * 1370 * @param containerType the container type 1371 * 1372 * @return the bean with the information about the container type 1373 */ 1374 public CmsContainerTypeInfoWrapper getContainerTypeInfo(String containerType) { 1375 1376 return new CmsContainerTypeInfoWrapper(this, m_cms, m_config, containerType); 1377 } 1378 1379 /** 1380 * Gets the CmsObject from the current Flex controller. 1381 * 1382 * @return the CmsObject from the current Flex controller 1383 */ 1384 public CmsObject getControllerCms() { 1385 1386 return CmsFlexController.getController(m_request).getCmsObject(); 1387 } 1388 1389 /** 1390 * Returns the current detail content, or <code>null</code> if no detail content is requested.<p> 1391 * 1392 * @return the current detail content, or <code>null</code> if no detail content is requested.<p> 1393 */ 1394 public CmsJspResourceWrapper getDetailContent() { 1395 1396 return CmsJspResourceWrapper.wrap(m_cms, m_detailContentResource); 1397 } 1398 1399 /** 1400 * Returns the structure id of the current detail content, or <code>null</code> if no detail content is requested.<p> 1401 * 1402 * @return the structure id of the current detail content, or <code>null</code> if no detail content is requested.<p> 1403 */ 1404 public CmsUUID getDetailContentId() { 1405 1406 return m_detailContentResource == null ? null : m_detailContentResource.getStructureId(); 1407 } 1408 1409 /** 1410 * Returns the detail content site path, or <code>null</code> if not available.<p> 1411 * 1412 * @return the detail content site path 1413 */ 1414 public String getDetailContentSitePath() { 1415 1416 return ((m_cms == null) || (m_detailContentResource == null)) 1417 ? null 1418 : m_cms.getSitePath(m_detailContentResource); 1419 } 1420 1421 /** 1422 * Returns the detail function page.<p> 1423 * 1424 * @return the detail function page 1425 */ 1426 public CmsJspResourceWrapper getDetailFunctionPage() { 1427 1428 return CmsJspResourceWrapper.wrap(m_cms, m_detailFunctionPage); 1429 } 1430 1431 /** 1432 * Returns the detail only page.<p> 1433 * 1434 * @return the detail only page 1435 */ 1436 public CmsContainerPageBean getDetailOnlyPage() { 1437 1438 if ((null == m_detailOnlyPage) && (null != m_detailContentResource)) { 1439 String pageRootPath = m_cms.getRequestContext().addSiteRoot(m_cms.getRequestContext().getUri()); 1440 m_detailOnlyPage = CmsDetailOnlyContainerUtil.getDetailOnlyPage(m_cms, m_request, pageRootPath, false); 1441 } 1442 return m_detailOnlyPage; 1443 } 1444 1445 /** 1446 * Returns the currently rendered element.<p> 1447 * 1448 * @return the currently rendered element 1449 */ 1450 public CmsContainerElementWrapper getElement() { 1451 1452 return m_element != null ? new CmsContainerElementWrapper(() -> m_element) : null; 1453 } 1454 1455 /** 1456 * Returns a lazy initialized map of wrapped container elements beans by container name suffix.<p> 1457 * 1458 * So in case there is more than one container where the name end with the given suffix, 1459 * a joined list of container elements beans is returned.<p> 1460 * 1461 * @return a lazy initialized map of wrapped container elements beans by container name suffix 1462 * 1463 * @see #getElementsInContainer() 1464 */ 1465 public Map<String, List<CmsContainerElementWrapper>> getElementBeansInContainers() { 1466 1467 return CmsCollectionsGenericWrapper.createLazyMap(obj -> { 1468 if (obj instanceof String) { 1469 List<CmsContainerElementBean> containerElements = new ArrayList<>(); 1470 for (CmsContainerBean container : getPage().getContainers().values()) { 1471 if (container.getName().endsWith("-" + obj)) { 1472 for (CmsContainerElementBean element : container.getElements()) { 1473 try { 1474 element.initResource(m_cms); 1475 containerElements.add(new CmsContainerElementWrapper(() -> element)); 1476 } catch (Exception e) { 1477 LOG.error(e.getLocalizedMessage(), e); 1478 } 1479 } 1480 } 1481 } 1482 return containerElements; 1483 } else { 1484 return null; 1485 } 1486 }); 1487 1488 } 1489 1490 /** 1491 * Returns a lazy initialized map of wrapped element resources by container name.<p> 1492 * 1493 * @return the lazy map of element resource wrappers 1494 */ 1495 public Map<String, List<CmsJspResourceWrapper>> getElementsInContainer() { 1496 1497 return CmsCollectionsGenericWrapper.createLazyMap(obj -> { 1498 if (obj instanceof String) { 1499 List<CmsJspResourceWrapper> elements = new ArrayList<>(); 1500 CmsContainerBean container = getPage().getContainers().get(obj); 1501 if (container != null) { 1502 for (CmsContainerElementBean element : container.getElements()) { 1503 try { 1504 element.initResource(m_cms); 1505 elements.add(CmsJspResourceWrapper.wrap(m_cms, element.getResource())); 1506 } catch (Exception e) { 1507 LOG.error(e.getLocalizedMessage(), e); 1508 } 1509 } 1510 } 1511 return elements; 1512 } else { 1513 return null; 1514 } 1515 }); 1516 1517 } 1518 1519 /** 1520 * Returns a lazy initialized map of wrapped element resources by container name suffix.<p> 1521 * 1522 * So in case there is more than one container where the name end with the given suffix, 1523 * a joined list of elements is returned.<p> 1524 * 1525 * @return the lazy map of element resource wrappers 1526 * 1527 * @see #getElementBeansInContainers() 1528 */ 1529 public Map<String, List<CmsJspResourceWrapper>> getElementsInContainers() { 1530 1531 return CmsCollectionsGenericWrapper.createLazyMap(obj -> { 1532 if (obj instanceof String) { 1533 List<CmsJspResourceWrapper> elements = new ArrayList<>(); 1534 for (CmsContainerBean container : getPage().getContainers().values()) { 1535 if (container.getName().endsWith("-" + obj)) { 1536 for (CmsContainerElementBean element : container.getElements()) { 1537 try { 1538 element.initResource(m_cms); 1539 elements.add(CmsJspResourceWrapper.wrap(m_cms, element.getResource())); 1540 } catch (Exception e) { 1541 LOG.error(e.getLocalizedMessage(), e); 1542 } 1543 } 1544 } 1545 } 1546 return elements; 1547 } else { 1548 return null; 1549 } 1550 }); 1551 1552 } 1553 1554 /** 1555 * Alternative method name for getReloadMarker(). 1556 * 1557 * @see org.opencms.jsp.util.CmsJspStandardContextBean#getReloadMarker() 1558 * 1559 * @return the reload marker 1560 */ 1561 public String getEnableReload() { 1562 1563 return getReloadMarker(); 1564 } 1565 1566 /** 1567 * Gets the formatter info wrapper for the given formatter key. 1568 * 1569 * @param formatterKey a formatter key 1570 * @return the formatter information for the formatter key, or null if no formatter was found 1571 */ 1572 public CmsFormatterInfoWrapper getFormatterInfo(String formatterKey) { 1573 1574 CmsObject cms = m_cms; 1575 CmsADEConfigData config = m_config; 1576 I_CmsFormatterBean formatter = config.findFormatter(formatterKey); 1577 if (formatter == null) { 1578 return null; 1579 } 1580 return new CmsFormatterInfoWrapper(cms, config, formatter); 1581 1582 } 1583 1584 /** 1585 * Gets the formatter bean for active formatters with a given container type. 1586 * 1587 * @param containerType the container type 1588 * @return the wrapped formatters 1589 */ 1590 public List<CmsFormatterInfoWrapper> getFormatterInfoForContainer(String containerType) { 1591 1592 return wrapFormatters(m_config.getActiveFormattersWithContainerType(containerType)); 1593 1594 } 1595 1596 /** 1597 * Gets the formatter beans for active formatters with a given display type. 1598 * 1599 * @param displayType the display type 1600 * @return the wrapped formatters 1601 */ 1602 public List<CmsFormatterInfoWrapper> getFormatterInfoForDisplay(String displayType) { 1603 1604 CmsADEConfigData config = m_config; 1605 return wrapFormatters(config.getActiveFormattersWithDisplayType(displayType)); 1606 } 1607 1608 /** 1609 * Gets a lazy map which can be used to access element setting defaults for a specific formatter key and setting name. 1610 * 1611 * @return the lazy map 1612 */ 1613 public Map<String, Map<String, CmsJspObjectValueWrapper>> getFormatterSettingDefault() { 1614 1615 return CmsCollectionsGenericWrapper.createLazyMap(input -> { 1616 String formatterKey = (String)input; 1617 I_CmsFormatterBean formatter = m_config.findFormatter(formatterKey); 1618 if (formatter == null) { 1619 return CmsCollectionsGenericWrapper.createLazyMap(input2 -> { 1620 return CmsJspObjectValueWrapper.NULL_VALUE_WRAPPER; 1621 }); 1622 } else { 1623 final Map<String, CmsXmlContentProperty> settingDefs = formatter.getSettings(m_config); 1624 return CmsCollectionsGenericWrapper.createLazyMap(input2 -> { 1625 String settingName = (String)input2; 1626 CmsXmlContentProperty settingDef = settingDefs.get(settingName); 1627 if (settingDef == null) { 1628 return CmsJspObjectValueWrapper.NULL_VALUE_WRAPPER; 1629 } else { 1630 String settingDefault = settingDef.getDefault(); 1631 return CmsJspObjectValueWrapper.createWrapper(m_cms, settingDefault); 1632 } 1633 }); 1634 } 1635 }); 1636 } 1637 1638 /** 1639 * Returns a lazy initialized Map which allows access to the dynamic function beans using the JSP EL.<p> 1640 * 1641 * When given a key, the returned map will look up the corresponding dynamic function bean in the module configuration.<p> 1642 * 1643 * @return a lazy initialized Map which allows access to the dynamic function beans using the JSP EL 1644 */ 1645 public Map<String, CmsDynamicFunctionBeanWrapper> getFunction() { 1646 1647 if (m_function == null) { 1648 1649 Transformer transformer = new Transformer() { 1650 1651 @Override 1652 public Object transform(Object input) { 1653 1654 try { 1655 CmsDynamicFunctionBean dynamicFunction = readDynamicFunctionBean((String)input); 1656 CmsDynamicFunctionBeanWrapper wrapper = new CmsDynamicFunctionBeanWrapper( 1657 m_cms, 1658 dynamicFunction); 1659 return wrapper; 1660 1661 } catch (CmsException e) { 1662 LOG.debug(e.getLocalizedMessage(), e); 1663 return new CmsDynamicFunctionBeanWrapper(m_cms, null); 1664 } 1665 } 1666 }; 1667 m_function = CmsCollectionsGenericWrapper.createLazyMap(transformer); 1668 } 1669 return m_function; 1670 1671 } 1672 1673 /** 1674 * Deprecated method to access function detail pages using the EL.<p> 1675 * 1676 * @return a lazy initialized Map that provides the detail page link as a value when given the name of a 1677 * (named) dynamic function as a key 1678 * 1679 * @deprecated use {@link #getFunctionDetailPage()} instead 1680 */ 1681 @Deprecated 1682 public Map<String, String> getFunctionDetail() { 1683 1684 return getFunctionDetailPage(); 1685 } 1686 1687 /** 1688 * Returns a lazy initialized Map that provides the detail page link as a value when given the name of a 1689 * (named) dynamic function as a key.<p> 1690 * 1691 * The provided Map key is assumed to be a String that represents a named dynamic function.<p> 1692 * 1693 * Usage example on a JSP with the JSTL:<pre> 1694 * <a href=${cms.functionDetailPage['search']} /> 1695 * </pre> 1696 * 1697 * @return a lazy initialized Map that provides the detail page link as a value when given the name of a 1698 * (named) dynamic function as a key 1699 * 1700 * @see #getTypeDetailPage() 1701 */ 1702 public Map<String, String> getFunctionDetailPage() { 1703 1704 if (m_functionDetailPage == null) { 1705 m_functionDetailPage = CmsCollectionsGenericWrapper.createLazyMap( 1706 new CmsDetailLookupTransformer(CmsDetailPageInfo.FUNCTION_PREFIX)); 1707 } 1708 return m_functionDetailPage; 1709 } 1710 1711 /** 1712 * Returns a lazy initialized Map that provides the detail page link as a value when given the name of a 1713 * (named) dynamic function as a key.<p> 1714 * 1715 * The provided Map key is assumed to be a String that represents a named dynamic function.<p> 1716 * 1717 * Usage example on a JSP with the JSTL:<pre> 1718 * <a href=${cms.functionDetailPage['search']} /> 1719 * </pre> 1720 * 1721 * @return a lazy initialized Map that provides the detail page link as a value when given the name of a 1722 * (named) dynamic function as a key 1723 * 1724 * @see #getTypeDetailPage() 1725 */ 1726 public Map<String, String> getFunctionDetailPageExact() { 1727 1728 if (m_functionDetailPageExact == null) { 1729 m_functionDetailPageExact = CmsCollectionsGenericWrapper.createLazyMap( 1730 name -> getFunctionDetailLinkExact(m_cms, (String)name)); 1731 } 1732 return m_functionDetailPageExact; 1733 } 1734 1735 /** 1736 * Returns a lazy map which creates a wrapper object for a dynamic function format when given an XML content 1737 * as a key.<p> 1738 * 1739 * @return a lazy map for accessing function formats for a content 1740 */ 1741 public Map<CmsJspContentAccessBean, CmsDynamicFunctionFormatWrapper> getFunctionFormatFromContent() { 1742 1743 Transformer transformer = new Transformer() { 1744 1745 @Override 1746 public Object transform(Object contentAccess) { 1747 1748 CmsXmlContent content = (CmsXmlContent)(((CmsJspContentAccessBean)contentAccess).getRawContent()); 1749 CmsDynamicFunctionParser parser = new CmsDynamicFunctionParser(); 1750 CmsDynamicFunctionBean functionBean = null; 1751 try { 1752 functionBean = parser.parseFunctionBean(m_cms, content); 1753 } catch (CmsException e) { 1754 LOG.debug(e.getLocalizedMessage(), e); 1755 return new CmsDynamicFunctionFormatWrapper(m_cms, null); 1756 } 1757 String type = getContainer().getType(); 1758 String width = getContainer().getWidth(); 1759 int widthNum = -1; 1760 try { 1761 widthNum = Integer.parseInt(width); 1762 } catch (NumberFormatException e) { 1763 LOG.debug(e.getLocalizedMessage(), e); 1764 } 1765 CmsDynamicFunctionBean.Format format = functionBean.getFormatForContainer(m_cms, type, widthNum); 1766 CmsDynamicFunctionFormatWrapper wrapper = new CmsDynamicFunctionFormatWrapper(m_cms, format); 1767 return wrapper; 1768 } 1769 }; 1770 return CmsCollectionsGenericWrapper.createLazyMap(transformer); 1771 } 1772 1773 /** 1774 * Returns <code>true</code> if the current page is a detail page.<p> 1775 * 1776 * @return <code>true</code> if the current page is a detail page 1777 */ 1778 public boolean getIsDetailPage() { 1779 1780 CmsJspResourceWrapper page = getPageResource(); 1781 return OpenCms.getADEManager().isDetailPage(m_cms, page); 1782 } 1783 1784 /** 1785 * Returns <code>true</code> if the current request is direct edit enabled.<p> 1786 * 1787 * Online-, history-requests, previews and temporary files will not be editable.<p> 1788 * 1789 * @return <code>true</code> if the current request is direct edit enabled 1790 */ 1791 public boolean getIsEditMode() { 1792 1793 if (m_isEditMode == null) { 1794 m_isEditMode = Boolean.valueOf(CmsJspTagEditable.isEditableRequest(m_request)); 1795 } 1796 return m_isEditMode.booleanValue() && !m_forceDisableEditMode; 1797 } 1798 1799 /** 1800 * Returns <code>true</code> if the current request is a JSON request.<p> 1801 * 1802 * @return <code>true</code> if we are in a JSON request 1803 */ 1804 public boolean getIsJSONRequest() { 1805 1806 return CmsJsonPartFilter.isJsonRequest(m_request); 1807 } 1808 1809 /** 1810 * Returns <code>true</code> if the current project is the online project.<p> 1811 * 1812 * @return <code>true</code> if the current project is the online project 1813 */ 1814 public boolean getIsOnlineProject() { 1815 1816 return m_cms.getRequestContext().getCurrentProject().isOnlineProject(); 1817 } 1818 1819 /** 1820 * Returns true if the current request is in direct edit preview mode.<p> 1821 * 1822 * This is the case if the request is not in edit mode and in the online project.<p> 1823 * 1824 * @return <code>true</code> if the current request is in direct edit preview mode 1825 */ 1826 public boolean getIsPreviewMode() { 1827 1828 return !getIsOnlineProject() && !getIsEditMode(); 1829 } 1830 1831 /** 1832 * Returns the current locale.<p> 1833 * 1834 * @return the current locale 1835 */ 1836 public Locale getLocale() { 1837 1838 return getRequestContext().getLocale(); 1839 } 1840 1841 /** 1842 * Gets a map providing access to the locale variants of the current page.<p> 1843 * 1844 * Note that all available locales for the site / subsite are used as keys, not just the ones for which a locale 1845 * variant actually exists. 1846 * 1847 * Usage in JSPs: ${cms.localeResource['de']] 1848 * 1849 * @return the map from locale strings to locale variant resources 1850 */ 1851 public Map<String, CmsJspResourceWrapper> getLocaleResource() { 1852 1853 Map<String, CmsJspResourceWrapper> result = getPageResource().getLocaleResource(); 1854 List<Locale> locales = CmsLocaleGroupService.getPossibleLocales(m_cms, getPageResource()); 1855 for (Locale locale : locales) { 1856 if (!result.containsKey(locale.toString())) { 1857 result.put(locale.toString(), null); 1858 } 1859 } 1860 return result; 1861 } 1862 1863 /** 1864 * Gets the main locale for the current page's locale group.<p> 1865 * 1866 * @return the main locale for the current page's locale group 1867 */ 1868 public Locale getMainLocale() { 1869 1870 return getPageResource().getMainLocale(); 1871 } 1872 1873 /** 1874 * Returns the meta mappings map.<p> 1875 * 1876 * @return the meta mappings 1877 */ 1878 public Map<String, String> getMeta() { 1879 1880 initMetaMappings(); 1881 return CmsCollectionsGenericWrapper.createLazyMap(new MetaLookupTranformer()); 1882 } 1883 1884 /** 1885 * Returns the currently displayed container page.<p> 1886 * 1887 * @return the currently displayed container page 1888 */ 1889 public CmsContainerPageBean getPage() { 1890 1891 if (null == m_page) { 1892 try { 1893 initPage(); 1894 } catch (CmsException e) { 1895 if (LOG.isWarnEnabled()) { 1896 LOG.warn(e, e); 1897 } 1898 } 1899 } 1900 return m_page; 1901 } 1902 1903 /** 1904 * Returns the container page bean for the give page and locale.<p> 1905 * 1906 * @param page the container page resource as id, path or already as resource 1907 * @param locale the content locale as locale or string 1908 * 1909 * @return the container page bean 1910 */ 1911 public CmsJspContainerPageWrapper getPage(Object page, Object locale) { 1912 1913 CmsResource pageResource = null; 1914 CmsContainerPageBean result = null; 1915 if (m_cms != null) { 1916 try { 1917 pageResource = CmsJspElFunctions.convertRawResource(m_cms, page); 1918 Locale l = CmsJspElFunctions.convertLocale(locale); 1919 1920 CmsADEConfigData adeConfig; 1921 if (OpenCms.getSiteManager().startsWithShared(pageResource.getRootPath())) { 1922 adeConfig = OpenCms.getADEManager().lookupConfiguration( 1923 m_cms, 1924 m_cms.getRequestContext().addSiteRoot(m_cms.getRequestContext().getUri())); 1925 } else { 1926 adeConfig = OpenCms.getADEManager().lookupConfiguration(m_cms, pageResource.getRootPath()); 1927 } 1928 1929 result = getPage(adeConfig, pageResource); 1930 if (result != null) { 1931 for (CmsContainerBean container : result.getContainers().values()) { 1932 for (CmsContainerElementBean element : container.getElements()) { 1933 boolean isGroupContainer = element.isGroupContainer(m_cms); 1934 boolean isInheritedContainer = element.isInheritedContainer(m_cms); 1935 I_CmsFormatterBean formatterConfig = null; 1936 if (!isGroupContainer && !isInheritedContainer) { 1937 element.initResource(m_cms); 1938 // ensure that the formatter configuration id is added to the element settings, so it will be persisted on save 1939 formatterConfig = CmsJspTagContainer.getFormatterConfigurationForElement( 1940 m_cms, 1941 element, 1942 adeConfig, 1943 container.getName(), 1944 "", 1945 0); 1946 if (formatterConfig != null) { 1947 element.initSettings(m_cms, adeConfig, formatterConfig, l, m_request, null); 1948 } 1949 } 1950 } 1951 } 1952 } 1953 } catch (Exception e) { 1954 LOG.warn(e.getLocalizedMessage(), e); 1955 } 1956 1957 } 1958 return new CmsJspContainerPageWrapper(m_cms, result); 1959 } 1960 1961 /** 1962 * Gets the id for the container page which the current element is located in. 1963 * 1964 * <p>In case the current container is a detail-only container, the id of the detail-only page will be returned. 1965 * 1966 * @return the id of the container page which the current element is located in 1967 */ 1968 public String getPageIdForElement() { 1969 1970 CmsObject cms = getCmsObject(); 1971 if (getElement().isInMemoryOnly() || isEdited()) { 1972 return null; 1973 } 1974 if (getContainer().isDetailOnly()) { 1975 CmsResource detailContent = getDetailContent(); 1976 CmsResource detailOnlyPage = CmsDetailOnlyContainerUtil.getDetailOnlyPage( 1977 cms, 1978 detailContent, 1979 "" + cms.getRequestContext().getLocale()).orNull(); 1980 if (detailOnlyPage != null) { 1981 return detailOnlyPage.getStructureId().toString(); 1982 } else { 1983 return null; 1984 } 1985 } else { 1986 return getPageResource().getStructureId().toString(); 1987 } 1988 } 1989 1990 /** 1991 * Returns the current container page resource.<p> 1992 * 1993 * @return the current container page resource 1994 */ 1995 public CmsJspResourceWrapper getPageResource() { 1996 1997 try { 1998 if (m_pageResource == null) { 1999 // get the container page itself, checking the history first 2000 m_pageResource = CmsJspResourceWrapper.wrap( 2001 m_cms, 2002 (CmsResource)CmsHistoryResourceHandler.getHistoryResource(m_request)); 2003 if (m_pageResource == null) { 2004 m_pageResource = CmsJspResourceWrapper.wrap( 2005 m_cms, 2006 m_cms.readResource( 2007 m_cms.getRequestContext().getUri(), 2008 CmsResourceFilter.ignoreExpirationOffline(m_cms))); 2009 } 2010 } 2011 } catch (CmsException e) { 2012 LOG.error(e.getLocalizedMessage(), e); 2013 } 2014 return m_pageResource; 2015 } 2016 2017 /** 2018 * Returns the parent container to the current container if available.<p> 2019 * 2020 * @return the parent container 2021 */ 2022 public CmsContainerBean getParentContainer() { 2023 2024 CmsContainerBean result = null; 2025 if ((getContainer() != null) && (getContainer().getParentInstanceId() != null)) { 2026 result = m_parentContainers.get(getContainer().getParentInstanceId()); 2027 } 2028 return result; 2029 } 2030 2031 /** 2032 * Returns the instance id parent container mapping.<p> 2033 * 2034 * @return the instance id parent container mapping 2035 */ 2036 public Map<String, CmsContainerBean> getParentContainers() { 2037 2038 if (m_parentContainers == null) { 2039 initPageData(); 2040 } 2041 return Collections.unmodifiableMap(m_parentContainers); 2042 } 2043 2044 /** 2045 * Returns the parent element to the current element if available.<p> 2046 * 2047 * @return the parent element or null 2048 */ 2049 public CmsContainerElementBean getParentElement() { 2050 2051 return getParentElement(getElement()); 2052 } 2053 2054 /** 2055 * Looks up a path dependent secret in the secret store for the current sitemap config path. 2056 * 2057 * <p>This is done as follows: 2058 * <p>For the root path of each subsitemap we are in, in ascending order, the key (prefix + "." + path) is looked up. 2059 * The first match will be returned. As a special case, the key for the site root is always looked up, even if it's not configured as 2060 * a sitemap. 2061 * 2062 * @param prefix the key prefix 2063 * @return the secret (or null, if nothing was found) 2064 */ 2065 public String getPathDependentSecret(String prefix) { 2066 2067 return getSitemapConfigInternal().getPathDependentSecret(prefix); 2068 } 2069 2070 /** 2071 * Gets the set of plugin group names. 2072 * 2073 * @return the set of plugin group names 2074 */ 2075 public Set<String> getPluginGroups() { 2076 2077 return getPlugins().keySet(); 2078 } 2079 2080 /** 2081 * Gets the map of plugins by group. 2082 * 2083 * @return the map of active plugins by group 2084 */ 2085 public Map<String, List<CmsTemplatePluginWrapper>> getPlugins() { 2086 2087 if (m_templatePlugins == null) { 2088 final Multimap<String, CmsTemplatePlugin> templatePluginsMultimap = new CmsTemplatePluginFinder( 2089 this).getTemplatePlugins(); 2090 Map<String, List<CmsTemplatePluginWrapper>> templatePlugins = new HashMap<>(); 2091 for (String key : templatePluginsMultimap.keySet()) { 2092 List<CmsTemplatePluginWrapper> wrappers = new ArrayList<>(); 2093 for (CmsTemplatePlugin plugin : templatePluginsMultimap.get(key)) { 2094 wrappers.add(new CmsTemplatePluginWrapper(m_cms, plugin)); 2095 } 2096 templatePlugins.put(key, Collections.unmodifiableList(wrappers)); 2097 } 2098 m_templatePlugins = templatePlugins; 2099 } 2100 return m_templatePlugins; 2101 } 2102 2103 /** 2104 * JSP EL accessor method for retrieving the preview formatters.<p> 2105 * 2106 * @return a lazy map for accessing preview formatters 2107 */ 2108 public Map<String, String> getPreviewFormatter() { 2109 2110 Transformer transformer = new Transformer() { 2111 2112 @Override 2113 public Object transform(Object uri) { 2114 2115 try { 2116 String rootPath = m_cms.getRequestContext().addSiteRoot((String)uri); 2117 CmsResource resource = m_cms.readResource((String)uri); 2118 CmsADEManager adeManager = OpenCms.getADEManager(); 2119 CmsADEConfigData configData = adeManager.lookupConfiguration(m_cms, rootPath); 2120 CmsFormatterConfiguration formatterConfig = configData.getFormatters(m_cms, resource); 2121 if (formatterConfig == null) { 2122 return ""; 2123 } 2124 I_CmsFormatterBean previewFormatter = formatterConfig.getPreviewFormatter(); 2125 if (previewFormatter == null) { 2126 return ""; 2127 } 2128 CmsUUID structureId = previewFormatter.getJspStructureId(); 2129 m_cms.readResource(structureId); 2130 CmsResource formatterResource = m_cms.readResource(structureId); 2131 String formatterSitePath = m_cms.getRequestContext().removeSiteRoot( 2132 formatterResource.getRootPath()); 2133 return formatterSitePath; 2134 } catch (CmsException e) { 2135 LOG.warn(e.getLocalizedMessage(), e); 2136 return ""; 2137 } 2138 } 2139 }; 2140 return CmsCollectionsGenericWrapper.createLazyMap(transformer); 2141 } 2142 2143 /** 2144 * Gets the unwrapped element. 2145 * 2146 * @return the unwrapped element 2147 */ 2148 public CmsContainerElementBean getRawElement() { 2149 2150 return m_element; 2151 } 2152 2153 /** 2154 * Reads all sub-categories below the provided category. 2155 * @return The map from the provided category to it's sub-categories in a {@link CmsJspCategoryAccessBean}. 2156 */ 2157 public Map<String, CmsJspCategoryAccessBean> getReadAllSubCategories() { 2158 2159 if (null == m_allSubCategories) { 2160 m_allSubCategories = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() { 2161 2162 @Override 2163 public Object transform(Object categoryPath) { 2164 2165 try { 2166 List<CmsCategory> categories = CmsCategoryService.getInstance().readCategories( 2167 m_cms, 2168 (String)categoryPath, 2169 true, 2170 m_cms.getRequestContext().getUri()); 2171 CmsJspCategoryAccessBean result = new CmsJspCategoryAccessBean( 2172 m_cms, 2173 categories, 2174 (String)categoryPath); 2175 return result; 2176 } catch (CmsException e) { 2177 LOG.warn(e.getLocalizedMessage(), e); 2178 return null; 2179 } 2180 } 2181 2182 }); 2183 } 2184 return m_allSubCategories; 2185 } 2186 2187 /** 2188 * Lazily reads the given attribute from the current sitemap or a property of the same name from the given resource. 2189 * 2190 * <p>Usage example: ${cms.readAttributeOrProperty['/index.html']['attr']} 2191 * 2192 * @return a lazy loading map for accessing attributes / properties 2193 */ 2194 public Map<String, Map<String, CmsJspObjectValueWrapper>> getReadAttributeOrProperty() { 2195 2196 if (m_attributesOrProperties == null) { 2197 m_attributesOrProperties = CmsCollectionsGenericWrapper.createLazyMap(pathObj -> { 2198 return CmsCollectionsGenericWrapper.createLazyMap(keyObj -> { 2199 2200 String path = (String)pathObj; 2201 String key = (String)keyObj; 2202 2203 CmsObject cms = getCmsObject(); 2204 String result = m_config.getAttribute(key, null); 2205 if (result == null) { 2206 try { 2207 CmsProperty prop = cms.readPropertyObject(path, key, /*search=*/true); 2208 result = prop.getValue(); 2209 } catch (CmsVfsResourceNotFoundException e) { 2210 LOG.info(e.getLocalizedMessage(), e); 2211 } catch (Exception e) { 2212 LOG.error(e.getLocalizedMessage(), e); 2213 } 2214 } 2215 return CmsJspObjectValueWrapper.createWrapper(cms, result); 2216 }); 2217 }); 2218 } 2219 return m_attributesOrProperties; 2220 } 2221 2222 /** 2223 * Reads the categories assigned to the currently requested URI. 2224 * @return the categories assigned to the currently requested URI. 2225 */ 2226 public CmsJspCategoryAccessBean getReadCategories() { 2227 2228 return getReadResourceCategories().get(getRequestContext().getRootUri()); 2229 } 2230 2231 /** 2232 * Transforms the category path of a category to the category. 2233 * @return a map from root or site path to category. 2234 */ 2235 public Map<String, CmsCategory> getReadCategory() { 2236 2237 if (null == m_categories) { 2238 m_categories = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() { 2239 2240 public Object transform(Object categoryPath) { 2241 2242 try { 2243 CmsCategoryService catService = CmsCategoryService.getInstance(); 2244 return catService.localizeCategory( 2245 m_cms, 2246 catService.readCategory(m_cms, (String)categoryPath, getRequestContext().getUri()), 2247 m_cms.getRequestContext().getLocale()); 2248 } catch (CmsException e) { 2249 LOG.warn(e.getLocalizedMessage(), e); 2250 return null; 2251 } 2252 } 2253 2254 }); 2255 } 2256 return m_categories; 2257 } 2258 2259 /** 2260 * Transforms the category path to the list of all categories on that path.<p> 2261 * 2262 * Example: For path <code>"location/europe/"</code> 2263 * the list <code>[getReadCategory.get("location/"),getReadCategory.get("location/europe/")]</code> 2264 * is returned. 2265 * @return a map from a category path to list of categories on that path. 2266 */ 2267 public Map<String, List<CmsCategory>> getReadPathCategories() { 2268 2269 if (null == m_pathCategories) { 2270 m_pathCategories = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() { 2271 2272 public Object transform(Object categoryPath) { 2273 2274 List<CmsCategory> result = new ArrayList<CmsCategory>(); 2275 2276 String path = (String)categoryPath; 2277 2278 if ((null == path) || (path.length() <= 1)) { 2279 return result; 2280 } 2281 2282 //cut last slash 2283 path = path.substring(0, path.length() - 1); 2284 2285 List<String> pathParts = Arrays.asList(path.split("/")); 2286 2287 String currentPath = ""; 2288 for (String part : pathParts) { 2289 currentPath += part + "/"; 2290 CmsCategory category = getReadCategory().get(currentPath); 2291 if (null != category) { 2292 result.add(category); 2293 } 2294 } 2295 return CmsCategoryService.getInstance().localizeCategories( 2296 m_cms, 2297 result, 2298 m_cms.getRequestContext().getLocale()); 2299 } 2300 2301 }); 2302 } 2303 return m_pathCategories; 2304 } 2305 2306 /** 2307 * Reads the categories assigned to a resource. 2308 * 2309 * @return map from the resource path (root path) to the assigned categories 2310 */ 2311 public Map<String, CmsJspCategoryAccessBean> getReadResourceCategories() { 2312 2313 if (null == m_resourceCategories) { 2314 m_resourceCategories = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() { 2315 2316 public Object transform(Object resourceName) { 2317 2318 try { 2319 CmsResource resource = m_cms.readResource( 2320 getRequestContext().removeSiteRoot((String)resourceName)); 2321 return new CmsJspCategoryAccessBean(m_cms, resource); 2322 } catch (CmsException e) { 2323 LOG.warn(e.getLocalizedMessage(), e); 2324 return null; 2325 } 2326 } 2327 }); 2328 } 2329 return m_resourceCategories; 2330 } 2331 2332 /** 2333 * Returns a HTML comment string that will cause the container page editor to reload the page if the element or its settings 2334 * were edited.<p> 2335 * 2336 * @return the reload marker 2337 */ 2338 public String getReloadMarker() { 2339 2340 if (m_cms.getRequestContext().getCurrentProject().isOnlineProject()) { 2341 return ""; // reload marker is not needed in Online mode 2342 } else { 2343 return CmsGwtConstants.FORMATTER_RELOAD_MARKER; 2344 } 2345 } 2346 2347 /** 2348 * Gets the stored request. 2349 * 2350 * @return the stored request 2351 */ 2352 public ServletRequest getRequest() { 2353 2354 return m_request; 2355 } 2356 2357 /** 2358 * Returns the request context.<p> 2359 * 2360 * @return the request context 2361 */ 2362 public CmsRequestContext getRequestContext() { 2363 2364 return m_cms.getRequestContext(); 2365 } 2366 2367 /** 2368 * Gets information about a specific resource type for use in JSPs. 2369 * 2370 * <p>If no type with the given name exists, null is returned. 2371 * 2372 * @param typeName the type name 2373 * @return the bean representing the resource type 2374 */ 2375 public CmsResourceTypeInfoWrapper getResourceTypeInfo(String typeName) { 2376 2377 try { 2378 I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(typeName); 2379 return new CmsResourceTypeInfoWrapper(this, m_cms, m_config, type); 2380 } catch (CmsLoaderException e) { 2381 LOG.info(e.getLocalizedMessage(), e); 2382 return null; 2383 } 2384 } 2385 2386 /** 2387 * Gets the schema information bean for the given type or XSD. 2388 * 2389 * @param typeOrXsd either the name of a resource type, or the VFS path to an XSD schema 2390 * @return the schema information bean 2391 * 2392 * @throws CmsException if something goes wrong 2393 */ 2394 public CmsSchemaInfo getSchemaInfo(String typeOrXsd) throws CmsException { 2395 2396 CmsXmlContentDefinition contentDef = null; 2397 if (OpenCms.getResourceManager().hasResourceType(typeOrXsd)) { 2398 contentDef = CmsXmlContentDefinition.getContentDefinitionForType(m_cms, typeOrXsd); 2399 } else if (typeOrXsd.startsWith("/")) { 2400 contentDef = CmsXmlContentDefinition.unmarshal(m_cms, typeOrXsd); 2401 } else { 2402 throw new IllegalArgumentException("Invalid getSchemaInfo argument: " + typeOrXsd); 2403 } 2404 CmsSchemaInfo info = new CmsSchemaInfo(m_cms, contentDef); 2405 return info; 2406 } 2407 2408 /** 2409 * Returns the current site.<p> 2410 * 2411 * @return the current site 2412 */ 2413 public CmsSite getSite() { 2414 2415 return OpenCms.getSiteManager().getSiteForSiteRoot(m_cms.getRequestContext().getSiteRoot()); 2416 } 2417 2418 /** 2419 * Gets the wrapper for the sitemap configuration. 2420 * 2421 * @return the wrapper object for the sitemap configuration 2422 */ 2423 public CmsJspSitemapConfigWrapper getSitemapConfig() { 2424 2425 return new CmsJspSitemapConfigWrapper(this); 2426 } 2427 2428 /** 2429 * Transforms root paths to site paths. 2430 * 2431 * @return lazy map from root paths to site paths. 2432 * 2433 * @see CmsRequestContext#removeSiteRoot(String) 2434 */ 2435 public Map<String, String> getSitePath() { 2436 2437 if (m_sitePaths == null) { 2438 m_sitePaths = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() { 2439 2440 public Object transform(Object rootPath) { 2441 2442 if (rootPath instanceof String) { 2443 return getRequestContext().removeSiteRoot((String)rootPath); 2444 } 2445 return null; 2446 } 2447 }); 2448 } 2449 return m_sitePaths; 2450 } 2451 2452 /** 2453 * Returns the subsite path for the currently requested URI.<p> 2454 * 2455 * @return the subsite path 2456 */ 2457 public String getSubSitePath() { 2458 2459 return m_cms.getRequestContext().removeSiteRoot( 2460 OpenCms.getADEManager().getSubSiteRoot(m_cms, m_cms.getRequestContext().getRootUri())); 2461 } 2462 2463 /** 2464 * Returns the system information.<p> 2465 * 2466 * @return the system information 2467 */ 2468 public CmsSystemInfo getSystemInfo() { 2469 2470 return OpenCms.getSystemInfo(); 2471 } 2472 2473 /** 2474 * Gets a bean containing information about the current template.<p> 2475 * 2476 * @return the template information bean 2477 */ 2478 public TemplateBean getTemplate() { 2479 2480 TemplateBean templateBean = getRequestAttribute(CmsTemplateContextManager.ATTR_TEMPLATE_BEAN); 2481 if (templateBean == null) { 2482 templateBean = new TemplateBean("", ""); 2483 } 2484 return templateBean; 2485 } 2486 2487 /** 2488 * Returns the title of a page delivered from OpenCms, usually used for the <code><title></code> tag of 2489 * a HTML page.<p> 2490 * 2491 * If no title information has been found, the empty String "" is returned.<p> 2492 * 2493 * @return the title of the current page 2494 */ 2495 public String getTitle() { 2496 2497 return getLocaleSpecificTitle(null); 2498 2499 } 2500 2501 /** 2502 * Get the title and read the Title property according the provided locale. 2503 * @return The map from locales to the locale specific titles. 2504 */ 2505 public Map<String, String> getTitleLocale() { 2506 2507 if (m_localeTitles == null) { 2508 m_localeTitles = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() { 2509 2510 public Object transform(Object inputLocale) { 2511 2512 Locale locale = null; 2513 if (null != inputLocale) { 2514 if (inputLocale instanceof Locale) { 2515 locale = (Locale)inputLocale; 2516 } else if (inputLocale instanceof String) { 2517 try { 2518 locale = LocaleUtils.toLocale((String)inputLocale); 2519 } catch (IllegalArgumentException | NullPointerException e) { 2520 // do nothing, just go on without locale 2521 } 2522 } 2523 } 2524 return getLocaleSpecificTitle(locale); 2525 } 2526 2527 }); 2528 } 2529 return m_localeTitles; 2530 } 2531 2532 /** 2533 * Returns a lazy initialized Map that provides the detail page link as a value when given the name of a 2534 * resource type as a key.<p> 2535 * 2536 * The provided Map key is assumed to be the name of a resource type that has a detail page configured.<p> 2537 * 2538 * Usage example on a JSP with the JSTL:<pre> 2539 * <a href=${cms.typeDetailPage['bs-blog']} /> 2540 * </pre> 2541 * 2542 * @return a lazy initialized Map that provides the detail page link as a value when given the name of a 2543 * resource type as a key 2544 * 2545 * @see #getFunctionDetailPage() 2546 */ 2547 public Map<String, String> getTypeDetailPage() { 2548 2549 if (m_typeDetailPage == null) { 2550 m_typeDetailPage = CmsCollectionsGenericWrapper.createLazyMap(new CmsDetailLookupTransformer("")); 2551 } 2552 return m_typeDetailPage; 2553 } 2554 2555 /** 2556 * Returns an initialized VFS access bean.<p> 2557 * 2558 * @return an initialized VFS access bean 2559 */ 2560 public CmsJspVfsAccessBean getVfs() { 2561 2562 if (m_vfsBean == null) { 2563 // create a new VVFS access bean 2564 m_vfsBean = CmsJspVfsAccessBean.create(m_cms); 2565 } 2566 return m_vfsBean; 2567 } 2568 2569 /** 2570 * Returns the workplace locale from the current user's settings.<p> 2571 * 2572 * @return returns the workplace locale from the current user's settings 2573 */ 2574 public Locale getWorkplaceLocale() { 2575 2576 return OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms); 2577 } 2578 2579 /** 2580 * Returns an EL access wrapper map for the given object.<p> 2581 * 2582 * If the object is a {@link CmsResource}, then a {@link CmsJspResourceWrapper} is returned. 2583 * Otherwise the object is wrapped in a {@link CmsJspObjectValueWrapper}.<p> 2584 * 2585 * If the object is already is a wrapper, it is returned unchanged.<p> 2586 * 2587 * @return an EL access wrapper map for the given object 2588 */ 2589 public Map<Object, Object> getWrap() { 2590 2591 return CmsCollectionsGenericWrapper.createLazyMap(obj -> wrap(obj)); 2592 } 2593 2594 /** 2595 * Initializes the requested container page.<p> 2596 * 2597 * @throws CmsException in case reading the requested resource fails 2598 */ 2599 public void initPage() throws CmsException { 2600 2601 if ((m_page == null) && (m_cms != null)) { 2602 String requestUri = m_cms.getRequestContext().getUri(); 2603 // get the container page itself, checking the history first 2604 CmsResource pageResource = (CmsResource)CmsHistoryResourceHandler.getHistoryResource(m_request); 2605 if (pageResource == null) { 2606 pageResource = m_cms.readResource(requestUri, CmsResourceFilter.ignoreExpirationOffline(m_cms)); 2607 } 2608 m_config = OpenCms.getADEManager().lookupConfigurationWithCache(m_cms, pageResource.getRootPath()); 2609 m_page = getPage(m_config, pageResource); 2610 m_page = CmsTemplateMapper.get(m_request).transformContainerpageBean( 2611 m_cms, 2612 m_page, 2613 pageResource.getRootPath()); 2614 2615 } 2616 } 2617 2618 /** 2619 * Returns <code>true</code in case a detail page is available for the current element.<p> 2620 * 2621 * @return <code>true</code in case a detail page is available for the current element 2622 */ 2623 public boolean isDetailPageAvailable() { 2624 2625 boolean result = false; 2626 if ((m_cms != null) 2627 && (m_element != null) 2628 && !m_element.isInMemoryOnly() 2629 && (m_element.getResource() != null)) { 2630 try { 2631 String detailPage = OpenCms.getADEManager().getDetailPageHandler().getDetailPage( 2632 m_cms, 2633 m_element.getResource().getRootPath(), 2634 m_cms.getRequestContext().getUri(), 2635 null); 2636 result = detailPage != null; 2637 } catch (Exception e) { 2638 LOG.warn(e.getLocalizedMessage(), e); 2639 } 2640 } 2641 return result; 2642 } 2643 2644 /** 2645 * Returns <code>true</code> if this is a request to a detail resource, <code>false</code> otherwise.<p> 2646 * 2647 * Same as to check if {@link #getDetailContent()} is <code>null</code>.<p> 2648 * 2649 * @return <code>true</code> if this is a request to a detail resource, <code>false</code> otherwise 2650 */ 2651 public boolean isDetailRequest() { 2652 2653 return m_detailContentResource != null; 2654 } 2655 2656 /** 2657 * Returns if the page is in drag mode.<p> 2658 * 2659 * @return if the page is in drag mode 2660 */ 2661 public boolean isDragMode() { 2662 2663 return m_isDragMode; 2664 } 2665 2666 /** 2667 * Returns the flag to indicate if in drag and drop mode.<p> 2668 * 2669 * @return <code>true</code> if in drag and drop mode 2670 */ 2671 public boolean isEdited() { 2672 2673 return m_edited; 2674 } 2675 2676 /** 2677 * Checks if the flag that forces edit mode to be disabled is set. 2678 * 2679 * @return true if the flag that disables edit mode is set 2680 */ 2681 public boolean isForceDisableEditMode() { 2682 2683 return m_forceDisableEditMode; 2684 } 2685 2686 /** 2687 * Checks if the link is a link to a path in a different OpenCms site from the current one. 2688 * 2689 * @param link the link to check 2690 * @return true if the link is a link to different subsite 2691 */ 2692 public boolean isLinkToDifferentSite(String link) { 2693 2694 CmsObject cms = getControllerCms(); 2695 try { 2696 URI uri = new URI(link); 2697 if (uri.getScheme() != null) { 2698 String sitePart = uri.getScheme() + "://" + uri.getAuthority(); 2699 CmsSiteMatcher matcher = new CmsSiteMatcher(sitePart); 2700 CmsSite site = OpenCms.getSiteManager().matchSite(matcher); 2701 return ((site != null) && !site.getSiteRoot().equals(cms.getRequestContext().getSiteRoot())); 2702 } else { 2703 return false; 2704 } 2705 } catch (URISyntaxException e) { 2706 return false; 2707 } 2708 } 2709 2710 /** 2711 * Checks if the link is a link to a path in a different OpenCms subsite from the current one. 2712 * 2713 * <p>For detail links, this checks the subsite of the detail page, not the subsite of the detail content. 2714 * 2715 * @param link the link to check 2716 * @return true if the link is a link to different site 2717 */ 2718 public boolean isLinkToDifferentSubSite(String link) { 2719 2720 CmsObject cms = getControllerCms(); 2721 String subSite = CmsLinkManager.getLinkSubsite(cms, link); 2722 String currentRootPath = cms.getRequestContext().addSiteRoot(cms.getRequestContext().getUri()); 2723 boolean result = (subSite != null) 2724 && !subSite.equals(OpenCms.getADEManager().getSubSiteRoot(cms, currentRootPath)); 2725 return result; 2726 } 2727 2728 /** 2729 * Returns if the current element is a model group.<p> 2730 * 2731 * @return <code>true</code> if the current element is a model group 2732 */ 2733 public boolean isModelGroupElement() { 2734 2735 return (m_element != null) && !m_element.isInMemoryOnly() && isModelGroupPage() && m_element.isModelGroup(); 2736 } 2737 2738 /** 2739 * Returns if the current page is used to manage model groups.<p> 2740 * 2741 * @return <code>true</code> if the current page is used to manage model groups 2742 */ 2743 public boolean isModelGroupPage() { 2744 2745 CmsResource page = getPageResource(); 2746 return (page != null) && CmsContainerpageService.isEditingModelGroups(m_cms, page); 2747 2748 } 2749 2750 /** 2751 * Gets the link wrapper for the given path. 2752 * 2753 * @param path the path 2754 * @return the link wrapper 2755 */ 2756 public CmsJspLinkWrapper link(String path) { 2757 2758 return CmsJspObjectValueWrapper.createWrapper(m_cms, path).getToLink(); 2759 2760 } 2761 2762 /** 2763 * Replaces the current element with a copy to which some settings are added. 2764 * 2765 * <p>The original container element bean is not modified, and the bean is not replaced in any container beans, only the bean returned by the getElement() method is different. 2766 * 2767 * @param settings the settings to add 2768 */ 2769 public void modifySettings(Map<String, String> settings) { 2770 2771 if (m_element != null) { 2772 if ((settings != null) && !settings.isEmpty()) { 2773 m_element = m_element.clone(); 2774 if (m_element.getSettings() != null) { 2775 m_element.getSettings().putAll(settings); 2776 } else { 2777 LOG.error("Trying to modify null settings:" + m_element.getInstanceId()); 2778 } 2779 } 2780 } 2781 } 2782 2783 /** 2784 * Gets the resource wrapper for a given path or id. 2785 * 2786 * @param str a path or structure id 2787 * @return the wrapper for the resource with the given path or id 2788 */ 2789 public CmsJspResourceWrapper readResource(String str) { 2790 2791 return getVfs().getReadResource().get(str); 2792 2793 } 2794 2795 /** 2796 * Reads an XML content and returns it as a content access bean 2797 * @param str path or id 2798 * @return the content access bean for the content with the given path or id 2799 */ 2800 public CmsJspContentAccessBean readXml(String str) { 2801 2802 return getVfs().getReadXml().get(str); 2803 } 2804 2805 /** 2806 * Renders the elements of container in a container page wrapper as HTML (without a surrounding element). 2807 * 2808 * @param page the page wrapper 2809 * @param name the name or name prefix of the container 2810 * @return the rendered HTML 2811 */ 2812 public String renderContainer(CmsJspContainerPageWrapper page, String name) { 2813 2814 String result = page.renderContainer(this, name); 2815 return result; 2816 } 2817 2818 /** 2819 * Sets the container the currently rendered element is part of.<p> 2820 * 2821 * @param container the container the currently rendered element is part of 2822 */ 2823 public void setContainer(CmsContainerBean container) { 2824 2825 m_container = container; 2826 } 2827 2828 /** 2829 * Sets the detail only page.<p> 2830 * 2831 * @param detailOnlyPage the detail only page to set 2832 */ 2833 public void setDetailOnlyPage(CmsContainerPageBean detailOnlyPage) { 2834 2835 m_detailOnlyPage = detailOnlyPage; 2836 clearPageData(); 2837 } 2838 2839 /** 2840 * Sets if the page is in drag mode.<p> 2841 * 2842 * @param isDragMode if the page is in drag mode 2843 */ 2844 public void setDragMode(boolean isDragMode) { 2845 2846 m_isDragMode = isDragMode; 2847 } 2848 2849 /** 2850 * Sets the flag to indicate if in drag and drop mode.<p> 2851 * 2852 * @param edited <code>true</code> if in drag and drop mode 2853 */ 2854 public void setEdited(boolean edited) { 2855 2856 m_edited = edited; 2857 } 2858 2859 /** 2860 * In edit mode, creates a meta tag that tells the form-based content editor to use the stylesheet with the given path as a default. 2861 * 2862 * <p>Does nothing outside of edit mode. 2863 * 2864 * @param path the site path of a style sheet 2865 * @return the meta tag 2866 */ 2867 public String setEditorCssPath(String path) { 2868 2869 if (getIsEditMode()) { 2870 return "\n<meta name=\"" 2871 + CmsGwtConstants.META_EDITOR_STYLESHEET 2872 + "\" content=\"" 2873 + CmsEncoder.escapeXml(path) 2874 + "\">\n"; 2875 } else { 2876 return ""; 2877 } 2878 } 2879 2880 /** 2881 * Sets the currently rendered element.<p> 2882 * 2883 * @param element the currently rendered element to set 2884 */ 2885 public void setElement(CmsContainerElementBean element) { 2886 2887 m_element = element; 2888 } 2889 2890 /** 2891 * Enables / disables the flag that forces edit mode to be disabled. 2892 * 2893 * @param forceDisableEditMode the new value for the flag 2894 */ 2895 public void setForceDisableEditMode(boolean forceDisableEditMode) { 2896 2897 m_forceDisableEditMode = forceDisableEditMode; 2898 } 2899 2900 /** 2901 * Sets the currently displayed container page.<p> 2902 * 2903 * @param page the currently displayed container page to set 2904 */ 2905 public void setPage(CmsContainerPageBean page) { 2906 2907 m_page = page; 2908 clearPageData(); 2909 } 2910 2911 /** 2912 * Converts the given object to a resource wrapper and returns it, or returns null if the conversion fails 2913 * @param obj the object to convert 2914 * @return the resource wrapper 2915 */ 2916 public CmsJspResourceWrapper toResource(Object obj) { 2917 2918 Object wrapper = wrap(obj); 2919 try { 2920 if (obj instanceof A_CmsJspValueWrapper) { 2921 return ((A_CmsJspValueWrapper)obj).getToResource(); 2922 } else if (obj instanceof CmsJspResourceWrapper) { 2923 return ((CmsJspResourceWrapper)obj).getToResource(); 2924 } else { 2925 // in case we add another wrapper with a getToResource method that doesn't extend A_CmsJspValueWrapper 2926 return (CmsJspResourceWrapper)wrapper.getClass().getMethod("getToResource").invoke(wrapper); 2927 } 2928 } catch (Exception e) { 2929 LOG.debug(e.getLocalizedMessage(), e); 2930 return null; 2931 } 2932 } 2933 2934 /** 2935 * Updates the internally stored OpenCms user context.<p> 2936 * 2937 * @param cms the new OpenCms user context 2938 */ 2939 public void updateCmsObject(CmsObject cms) { 2940 2941 try { 2942 m_cms = OpenCms.initCmsObject(cms); 2943 } catch (CmsException e) { 2944 LOG.error(e.getLocalizedMessage(), e); 2945 m_cms = cms; 2946 } 2947 try { 2948 m_config = OpenCms.getADEManager().lookupConfigurationWithCache(cms, cms.getRequestContext().getRootUri()); 2949 } catch (Exception e) { 2950 LOG.error(e.getLocalizedMessage(), e); 2951 } 2952 } 2953 2954 /** 2955 * Updates the standard context bean from the request.<p> 2956 * 2957 * @param cmsFlexRequest the request from which to update the data 2958 */ 2959 public void updateRequestData(CmsFlexRequest cmsFlexRequest) { 2960 2961 CmsResource detailRes = CmsDetailPageResourceHandler.getDetailResource(cmsFlexRequest); 2962 m_detailContentResource = detailRes; 2963 m_request = cmsFlexRequest; 2964 } 2965 2966 /** 2967 * Gets the path of either the detail content (if this is a detail request) or the current page if it's not a detail request. 2968 * 2969 * @return the URI of the page or detail content 2970 */ 2971 public String uri() { 2972 2973 return isDetailRequest() ? getDetailContent().getSitePath() : getRequestContext().getUri(); 2974 } 2975 2976 /** 2977 * Returns an EL access wrapper map for the given object.<p> 2978 * 2979 * If the object is a {@link CmsResource}, then a {@link CmsJspResourceWrapper} is returned. 2980 * Otherwise the object is wrapped in a {@link CmsJspObjectValueWrapper}.<p> 2981 * 2982 * If the object is already is a wrapper, it is returned unchanged.<p> 2983 * 2984 * @param obj the object to wrap 2985 * @return an EL access wrapper map for the given object 2986 */ 2987 public Object wrap(Object obj) { 2988 2989 if ((obj instanceof A_CmsJspValueWrapper) || (obj instanceof CmsJspResourceWrapper)) { 2990 return obj; 2991 } else if (obj instanceof CmsResource) { 2992 return CmsJspResourceWrapper.wrap(m_cms, (CmsResource)obj); 2993 } else { 2994 return CmsJspObjectValueWrapper.createWrapper(m_cms, obj); 2995 } 2996 } 2997 2998 /** 2999 * Accessor for the CmsObject. 3000 * 3001 * @return the CmsObject 3002 */ 3003 protected CmsObject getCmsObject() { 3004 3005 return m_cms; 3006 } 3007 3008 /** 3009 * Returns the formatter configuration to the given element.<p> 3010 * 3011 * @param element the element 3012 * 3013 * @return the formatter configuration 3014 */ 3015 protected I_CmsFormatterBean getElementFormatter(CmsContainerElementBean element) { 3016 3017 if (m_elementInstances == null) { 3018 initPageData(); 3019 } 3020 I_CmsFormatterBean formatter = null; 3021 CmsContainerBean container = m_parentContainers.get(element.getInstanceId()); 3022 if (container == null) { 3023 // use the current container 3024 container = getContainer(); 3025 } 3026 if (container != null) { 3027 String containerName = container.getName(); 3028 Map<String, String> settings = element.getSettings(); 3029 if (settings != null) { 3030 String formatterConfigId = settings.get(CmsFormatterConfig.getSettingsKeyForContainer(containerName)); 3031 I_CmsFormatterBean dynamicFmt = m_config.findFormatter(formatterConfigId); 3032 if (dynamicFmt != null) { 3033 formatter = dynamicFmt; 3034 } 3035 } 3036 if (formatter == null) { 3037 try { 3038 CmsResource resource = m_cms.readResource(m_cms.getRequestContext().getUri()); 3039 3040 CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration( 3041 m_cms, 3042 resource.getRootPath()); 3043 CmsFormatterConfiguration formatters = config.getFormatters(m_cms, resource); 3044 int width = -2; 3045 try { 3046 width = Integer.parseInt(container.getWidth()); 3047 } catch (Exception e) { 3048 LOG.debug(e.getLocalizedMessage(), e); 3049 } 3050 formatter = formatters.getDefaultSchemaFormatter(container.getType(), width); 3051 } catch (CmsException e1) { 3052 if (LOG.isWarnEnabled()) { 3053 LOG.warn(e1.getLocalizedMessage(), e1); 3054 } 3055 } 3056 } 3057 } 3058 return formatter; 3059 } 3060 3061 /** 3062 * Returns the title according to the given locale. 3063 * @param locale the locale for which the title should be read. 3064 * @return the title according to the given locale 3065 */ 3066 protected String getLocaleSpecificTitle(Locale locale) { 3067 3068 String result = null; 3069 3070 try { 3071 3072 if (isDetailRequest()) { 3073 // this is a request to a detail page 3074 CmsResource res = getDetailContent(); 3075 CmsFile file = m_cms.readFile(res); 3076 CmsXmlContent content = CmsXmlContentFactory.unmarshal(m_cms, file); 3077 result = content.getHandler().getTitleMapping(m_cms, content, m_cms.getRequestContext().getLocale()); 3078 if (result == null) { 3079 // title not found, maybe no mapping OR not available in the current locale 3080 // read the title of the detail resource as fall back (may contain mapping from another locale) 3081 result = m_cms.readPropertyObject( 3082 res, 3083 CmsPropertyDefinition.PROPERTY_TITLE, 3084 false, 3085 locale).getValue(); 3086 } 3087 } 3088 if (result == null) { 3089 // read the title of the requested resource as fall back 3090 result = m_cms.readPropertyObject( 3091 m_cms.getRequestContext().getUri(), 3092 CmsPropertyDefinition.PROPERTY_TITLE, 3093 true, 3094 locale).getValue(); 3095 } 3096 } catch (CmsException e) { 3097 LOG.debug(e.getLocalizedMessage(), e); 3098 } 3099 if (CmsStringUtil.isEmptyOrWhitespaceOnly(result)) { 3100 result = ""; 3101 } 3102 3103 return result; 3104 } 3105 3106 /** 3107 * Returns the parent element if available.<p> 3108 * 3109 * @param element the element 3110 * 3111 * @return the parent element or null 3112 */ 3113 protected CmsContainerElementBean getParentElement(CmsContainerElementBean element) { 3114 3115 if (m_elementInstances == null) { 3116 initPageData(); 3117 } 3118 CmsContainerElementBean parent = null; 3119 CmsContainerBean cont = m_parentContainers.get(element.getInstanceId()); 3120 if ((cont != null) && cont.isNestedContainer()) { 3121 parent = m_elementInstances.get(cont.getParentInstanceId()); 3122 } 3123 return parent; 3124 } 3125 3126 /** 3127 * Accessor for the sitemap configuration. 3128 * 3129 * @return the sitemap configuration 3130 */ 3131 protected CmsADEConfigData getSitemapConfigInternal() { 3132 3133 return m_config; 3134 } 3135 3136 /** 3137 * Reads a dynamic function bean, given its name in the module configuration.<p> 3138 * 3139 * @param configuredName the name of the dynamic function in the module configuration 3140 * @return the dynamic function bean for the dynamic function configured under that name 3141 * 3142 * @throws CmsException if something goes wrong 3143 */ 3144 protected CmsDynamicFunctionBean readDynamicFunctionBean(String configuredName) throws CmsException { 3145 3146 CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration( 3147 m_cms, 3148 m_cms.addSiteRoot(m_cms.getRequestContext().getUri())); 3149 CmsFunctionReference functionRef = config.getFunctionReference(configuredName); 3150 if (functionRef == null) { 3151 return null; 3152 } 3153 CmsDynamicFunctionParser parser = new CmsDynamicFunctionParser(); 3154 CmsResource functionResource = m_cms.readResource(functionRef.getStructureId()); 3155 CmsDynamicFunctionBean result = parser.parseFunctionBean(m_cms, functionResource); 3156 return result; 3157 } 3158 3159 /** 3160 * Wraps a list of formatter beans for use in JSPs. 3161 * 3162 * @param formatters the formatters to wrap 3163 * @return the wrapped formatters 3164 */ 3165 protected List<CmsFormatterInfoWrapper> wrapFormatters(Collection<? extends I_CmsFormatterBean> formatters) { 3166 3167 List<I_CmsFormatterBean> formattersToSort = new ArrayList<>(formatters); 3168 List<CmsResourceTypeConfig> types = m_config.getResourceTypes(); 3169 3170 // we want to 'group' the returned formatters by resource type, which is slightly 3171 // complicated by the fact that formatters can support multiple resource types. 3172 3173 // first build a map that records the positions of the resource types configured in the sitemap configuration 3174 Map<String, Integer> typePositionsByTypeName = new HashMap<>(); 3175 for (int i = 0; i < types.size(); i++) { 3176 CmsResourceTypeConfig singleType = types.get(i); 3177 typePositionsByTypeName.put(singleType.getTypeName(), Integer.valueOf(i)); 3178 } 3179 // for each formatter, save the lowest position of any resource type it supports 3180 Map<String, Integer> lowestResourceTypePositionsByFormatterId = new HashMap<>(); 3181 for (I_CmsFormatterBean formatter : formatters) { 3182 int pos = Integer.MAX_VALUE; 3183 for (String typeName : formatter.getResourceTypeNames()) { 3184 Integer typeOrder = typePositionsByTypeName.get(typeName); 3185 if (typeOrder != null) { 3186 pos = Math.min(pos, typeOrder.intValue()); 3187 } 3188 } 3189 lowestResourceTypePositionsByFormatterId.put(formatter.getId(), Integer.valueOf(pos)); 3190 } 3191 3192 // now we can group the formatters by types using sorting, and inside the groups we sort by formatter rank 3193 Collections.sort(formattersToSort, new Comparator<I_CmsFormatterBean>() { 3194 3195 public int compare(I_CmsFormatterBean o1, I_CmsFormatterBean o2) { 3196 3197 return ComparisonChain.start().compare(getTypePosition(o1), getTypePosition(o2)).compare( 3198 o2.getRank(), 3199 o1.getRank()).result(); 3200 } 3201 3202 public int getTypePosition(I_CmsFormatterBean formatter) { 3203 3204 return lowestResourceTypePositionsByFormatterId.computeIfAbsent( 3205 formatter.getId(), 3206 id -> Integer.valueOf(Integer.MAX_VALUE)); 3207 } 3208 3209 }); 3210 return formattersToSort.stream().map( 3211 formatter -> new CmsFormatterInfoWrapper(m_cms, m_config, formatter)).collect(Collectors.toList()); 3212 3213 } 3214 3215 /** 3216 * Adds the mappings of the given formatter configuration.<p> 3217 * 3218 * @param formatterBean the formatter bean 3219 * @param elementId the element content structure id 3220 * @param resolver The macro resolver used on key and default value of the mappings 3221 * @param isDetailContent in case of a detail content 3222 */ 3223 private void addMappingsForFormatter( 3224 I_CmsFormatterBean formatterBean, 3225 CmsUUID elementId, 3226 CmsMacroResolver resolver, 3227 boolean isDetailContent) { 3228 3229 if ((formatterBean != null) && (formatterBean.getMetaMappings() != null)) { 3230 for (CmsMetaMapping map : formatterBean.getMetaMappings()) { 3231 String key = map.getKey(); 3232 key = resolver.resolveMacros(key); 3233 // the detail content mapping overrides other mappings 3234 if (isDetailContent 3235 || !m_metaMappings.containsKey(key) 3236 || (m_metaMappings.get(key).m_order <= map.getOrder())) { 3237 MetaMapping mapping = new MetaMapping(); 3238 mapping.m_key = key; 3239 mapping.m_elementXPath = map.getElement(); 3240 mapping.m_defaultValue = resolver.resolveMacros(map.getDefaultValue()); 3241 mapping.m_order = map.getOrder(); 3242 mapping.m_contentId = elementId; 3243 m_metaMappings.put(key, mapping); 3244 } 3245 } 3246 } 3247 } 3248 3249 /** 3250 * Clears the page element data.<p> 3251 */ 3252 private void clearPageData() { 3253 3254 m_elementInstances = null; 3255 m_parentContainers = null; 3256 } 3257 3258 /** 3259 * Finds the first ancestor of a resource matching a given predicate. 3260 * 3261 * @param cms the CMS context 3262 * @param resource the resource 3263 * @param predicate the predicate to test 3264 * 3265 * @return the first ancestor matching the predicate (which may possibly be the given resource itself), or null if no matching ancestor is found 3266 * @throws CmsException 3267 */ 3268 private CmsResource findAncestor(CmsObject cms, CmsResource resource, Predicate<CmsResource> predicate) { 3269 3270 try { 3271 CmsObject rootCms = OpenCms.initCmsObject(cms); 3272 rootCms.getRequestContext().setSiteRoot(""); 3273 CmsResource ancestor = resource; 3274 while (ancestor != null) { 3275 if (predicate.test(ancestor)) { 3276 return ancestor; 3277 } 3278 String parentFolder = CmsResource.getParentFolder(ancestor.getRootPath()); 3279 if (parentFolder == null) { 3280 break; 3281 } 3282 try { 3283 ancestor = rootCms.readResource(parentFolder, CmsResourceFilter.IGNORE_EXPIRATION); 3284 } catch (CmsException e) { 3285 LOG.info(e.getLocalizedMessage(), e); 3286 break; 3287 } 3288 } 3289 } catch (CmsException e) { 3290 LOG.error(e.getLocalizedMessage(), e); 3291 } 3292 return null; 3293 } 3294 3295 /** 3296 * Generates a link to the bundle editor to edit the provided message key. 3297 * The back link for the editor is the current uri with the provided backLinkAnchor added as anchor. 3298 * 3299 * If the bundle resource for the key could not be found, <code>null</code> is returned. 3300 * 3301 * @param messageKey the message key to open the bundle editor for. 3302 * @param backLinkAnchor the anchor id to add to the backlink to the page. If <code>null</code> no anchor is added to the backlink. 3303 * @param backLinkParams request parameters to add to the backlink without leading '?', e.g. "param1=a¶m2=b". 3304 * @param bundleName the name of the bundle to search the key in. If <code>null</code> the bundle is detected automatically. 3305 * @param nameFilters if more than one bundle is matched, bundles that match (substring matching) at least one of the provided strings are preferred. 3306 * This option is only useful, if the bundleName is not provided. 3307 * 3308 * @return a link to the bundle editor for editing the provided key, or <code>null</code> if the bundle for the key could not be found. 3309 */ 3310 private String getBundleEditorLink( 3311 String messageKey, 3312 String backLinkAnchor, 3313 String backLinkParams, 3314 String bundleName, 3315 List<String> nameFilters) { 3316 3317 if (!m_cms.getRequestContext().getCurrentProject().isOnlineProject()) { 3318 String filePath = null; 3319 if (null == bundleName) { 3320 filePath = getBundleRootPath(messageKey, nameFilters); 3321 } else { 3322 ResourceBundle bundle = CmsResourceBundleLoader.getBundle( 3323 bundleName, 3324 m_cms.getRequestContext().getLocale()); 3325 if (bundle instanceof CmsVfsResourceBundle) { 3326 CmsVfsResourceBundle vfsBundle = (CmsVfsResourceBundle)bundle; 3327 filePath = vfsBundle.getParameters().getBasePath(); 3328 } 3329 } 3330 try { 3331 if (null == filePath) { 3332 throw new Exception("Could not determine the VFS root path of the bundle."); 3333 } 3334 CmsUUID structureId = m_cms.readResource( 3335 m_cms.getRequestContext().removeSiteRoot(filePath)).getStructureId(); 3336 String backLink = OpenCms.getLinkManager().getServerLink(m_cms, m_cms.getRequestContext().getUri()); 3337 if (!((null == backLinkParams) || backLinkParams.isEmpty())) { 3338 backLink = backLink + "?" + backLinkParams; 3339 } 3340 if (!((null == backLinkAnchor) || backLinkAnchor.isEmpty())) { 3341 backLink = backLink + "#" + backLinkAnchor; 3342 } 3343 String appState = CmsEditor.getEditState(structureId, false, backLink); 3344 if (null != messageKey) { 3345 appState = A_CmsWorkplaceApp.addParamToState( 3346 appState, 3347 CmsMessageBundleEditor.PARAM_KEYFILTER, 3348 messageKey); 3349 } 3350 String link = CmsVaadinUtils.getWorkplaceLink(CmsEditorConfiguration.APP_ID, appState); 3351 return link; 3352 } catch (Throwable t) { 3353 if (LOG.isWarnEnabled()) { 3354 String message = "Failed to open bundle editor for key '" 3355 + messageKey 3356 + "' and bundle with name '" 3357 + bundleName 3358 + "'."; 3359 if (LOG.isDebugEnabled()) { 3360 LOG.debug(message, t); 3361 } else { 3362 LOG.warn(message); 3363 } 3364 } 3365 } 3366 } 3367 return null; 3368 } 3369 3370 /** 3371 * Returns the container page bean for the give resource.<p> 3372 * 3373 * @param config the sitemap config to use 3374 * @param pageResource the resource 3375 * 3376 * @return the container page bean 3377 * 3378 * @throws CmsException in case reading the page bean fails 3379 */ 3380 private CmsContainerPageBean getPage(CmsADEConfigData config, CmsResource pageResource) throws CmsException { 3381 3382 CmsContainerPageBean result = null; 3383 if ((pageResource != null) && CmsResourceTypeXmlContainerPage.isContainerPage(pageResource)) { 3384 CmsXmlContainerPage xmlContainerPage = CmsXmlContainerPageFactory.unmarshal(m_cms, pageResource, m_request); 3385 result = xmlContainerPage.getContainerPage(m_cms); 3386 CmsModelGroupHelper modelHelper = new CmsModelGroupHelper( 3387 m_cms, 3388 config, 3389 CmsJspTagEditable.isEditableRequest(m_request) && (m_request instanceof HttpServletRequest) 3390 ? CmsADESessionCache.getCache((HttpServletRequest)m_request, m_cms) 3391 : null, 3392 CmsContainerpageService.isEditingModelGroups(m_cms, pageResource)); 3393 result = modelHelper.readModelGroups(xmlContainerPage.getContainerPage(m_cms)); 3394 } 3395 return result; 3396 } 3397 3398 /** 3399 * Convenience method for getting a request attribute without an explicit cast.<p> 3400 * 3401 * @param name the attribute name 3402 * @return the request attribute 3403 */ 3404 @SuppressWarnings("unchecked") 3405 private <A> A getRequestAttribute(String name) { 3406 3407 Object attribute = m_request.getAttribute(name); 3408 3409 return attribute != null ? (A)attribute : null; 3410 } 3411 3412 /** 3413 * Initializes the mapping configuration.<p> 3414 */ 3415 private void initMetaMappings() { 3416 3417 if (m_metaMappings == null) { 3418 m_metaMappings = new HashMap<String, MetaMapping>(); 3419 try { 3420 initPage(); 3421 CmsMacroResolver resolver = new CmsMacroResolver(); 3422 resolver.setKeepEmptyMacros(true); 3423 resolver.setCmsObject(m_cms); 3424 resolver.setMessages(OpenCms.getWorkplaceManager().getMessages(getLocale())); 3425 CmsResourceFilter filter = getIsEditMode() 3426 ? CmsResourceFilter.IGNORE_EXPIRATION 3427 : CmsResourceFilter.DEFAULT; 3428 if (m_page != null) { 3429 for (CmsContainerBean container : m_page.getContainers().values()) { 3430 for (CmsContainerElementBean element : container.getElements()) { 3431 String settingsKey = CmsFormatterConfig.getSettingsKeyForContainer(container.getName()); 3432 String formatterConfigId = element.getSettings() != null 3433 ? element.getSettings().get(settingsKey) 3434 : null; 3435 I_CmsFormatterBean formatterBean = null; 3436 formatterBean = m_config.findFormatter(formatterConfigId); 3437 if ((formatterBean != null) 3438 && formatterBean.useMetaMappingsForNormalElements() 3439 && m_cms.existsResource(element.getId(), filter)) { 3440 addMappingsForFormatter(formatterBean, element.getId(), resolver, false); 3441 } 3442 3443 } 3444 } 3445 } 3446 if (getDetailContentId() != null) { 3447 try { 3448 CmsResource detailContent = m_cms.readResource( 3449 getDetailContentId(), 3450 CmsResourceFilter.ignoreExpirationOffline(m_cms)); 3451 CmsFormatterConfiguration config = OpenCms.getADEManager().lookupConfiguration( 3452 m_cms, 3453 m_cms.getRequestContext().getRootUri()).getFormatters(m_cms, detailContent); 3454 for (I_CmsFormatterBean formatter : config.getDetailFormatters()) { 3455 addMappingsForFormatter(formatter, getDetailContentId(), resolver, true); 3456 } 3457 } catch (CmsException e) { 3458 LOG.error( 3459 Messages.get().getBundle().key( 3460 Messages.ERR_READING_REQUIRED_RESOURCE_1, 3461 getDetailContentId()), 3462 e); 3463 } 3464 } 3465 } catch (Exception e) { 3466 LOG.error(e.getLocalizedMessage(), e); 3467 } 3468 } 3469 } 3470 3471 /** 3472 * Initializes the page element data.<p> 3473 */ 3474 private void initPageData() { 3475 3476 m_elementInstances = new HashMap<String, CmsContainerElementBean>(); 3477 m_parentContainers = new HashMap<String, CmsContainerBean>(); 3478 if (m_page != null) { 3479 for (CmsContainerBean container : m_page.getContainers().values()) { 3480 for (CmsContainerElementBean element : container.getElements()) { 3481 m_elementInstances.put(element.getInstanceId(), element); 3482 m_parentContainers.put(element.getInstanceId(), container); 3483 try { 3484 if (element.isGroupContainer(m_cms) || element.isInheritedContainer(m_cms)) { 3485 List<CmsContainerElementBean> children; 3486 if (element.isGroupContainer(m_cms)) { 3487 children = CmsJspTagContainer.getGroupContainerElements( 3488 m_cms, 3489 element, 3490 m_request, 3491 container.getType()); 3492 } else { 3493 children = CmsJspTagContainer.getInheritedContainerElements(m_cms, element); 3494 } 3495 for (CmsContainerElementBean childElement : children) { 3496 m_elementInstances.put(childElement.getInstanceId(), childElement); 3497 m_parentContainers.put(childElement.getInstanceId(), container); 3498 } 3499 } 3500 } catch (CmsException e) { 3501 LOG.error(e.getLocalizedMessage(), e); 3502 } 3503 } 3504 } 3505 // also add detail only data 3506 if (m_detailOnlyPage != null) { 3507 for (CmsContainerBean container : m_detailOnlyPage.getContainers().values()) { 3508 for (CmsContainerElementBean element : container.getElements()) { 3509 m_elementInstances.put(element.getInstanceId(), element); 3510 m_parentContainers.put(element.getInstanceId(), container); 3511 } 3512 } 3513 } 3514 } 3515 } 3516 3517}