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