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