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 GmbH & Co. KG, 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; 029 030import org.opencms.ade.configuration.CmsADEConfigData; 031import org.opencms.ade.containerpage.CmsContainerpageService; 032import org.opencms.ade.containerpage.CmsDetailOnlyContainerUtil; 033import org.opencms.ade.containerpage.CmsElementUtil; 034import org.opencms.ade.containerpage.shared.CmsContainer; 035import org.opencms.ade.containerpage.shared.CmsContainerElement; 036import org.opencms.ade.containerpage.shared.CmsFormatterConfig; 037import org.opencms.file.CmsGroup; 038import org.opencms.file.CmsObject; 039import org.opencms.file.CmsResource; 040import org.opencms.file.CmsUser; 041import org.opencms.file.CmsVfsResourceNotFoundException; 042import org.opencms.flex.CmsFlexController; 043import org.opencms.gwt.shared.CmsGwtConstants; 044import org.opencms.gwt.shared.CmsTemplateContextInfo; 045import org.opencms.i18n.CmsEncoder; 046import org.opencms.jsp.CmsJspTagAddParams.ParamState; 047import org.opencms.jsp.util.CmsJspStandardContextBean; 048import org.opencms.jsp.util.CmsJspStandardContextBean.CmsContainerElementWrapper; 049import org.opencms.loader.CmsJspLoader; 050import org.opencms.loader.CmsLoaderException; 051import org.opencms.loader.CmsTemplateContext; 052import org.opencms.loader.CmsTemplateContextManager; 053import org.opencms.loader.I_CmsTemplateContextProvider; 054import org.opencms.main.CmsException; 055import org.opencms.main.CmsIllegalStateException; 056import org.opencms.main.CmsLog; 057import org.opencms.main.OpenCms; 058import org.opencms.security.CmsPermissionViolationException; 059import org.opencms.security.CmsRole; 060import org.opencms.util.CmsRequestUtil; 061import org.opencms.util.CmsStringUtil; 062import org.opencms.util.CmsUUID; 063import org.opencms.xml.containerpage.CmsADESessionCache; 064import org.opencms.xml.containerpage.CmsContainerBean; 065import org.opencms.xml.containerpage.CmsContainerElementBean; 066import org.opencms.xml.containerpage.CmsContainerPageBean; 067import org.opencms.xml.containerpage.CmsFormatterConfiguration; 068import org.opencms.xml.containerpage.CmsGroupContainerBean; 069import org.opencms.xml.containerpage.CmsXmlContainerPage; 070import org.opencms.xml.containerpage.CmsXmlContainerPageFactory; 071import org.opencms.xml.containerpage.CmsXmlGroupContainer; 072import org.opencms.xml.containerpage.CmsXmlGroupContainerFactory; 073import org.opencms.xml.containerpage.CmsXmlInheritGroupContainerHandler; 074import org.opencms.xml.containerpage.I_CmsFormatterBean; 075import org.opencms.xml.templatemapper.CmsTemplateMapper; 076 077import java.io.IOException; 078import java.util.ArrayList; 079import java.util.Collections; 080import java.util.HashMap; 081import java.util.List; 082import java.util.Locale; 083import java.util.Map; 084import java.util.Map.Entry; 085 086import javax.servlet.ServletRequest; 087import javax.servlet.ServletResponse; 088import javax.servlet.http.HttpServletRequest; 089import javax.servlet.http.HttpServletResponse; 090import javax.servlet.jsp.JspException; 091import javax.servlet.jsp.tagext.BodyContent; 092import javax.servlet.jsp.tagext.BodyTagSupport; 093import javax.servlet.jsp.tagext.TryCatchFinally; 094 095import org.apache.commons.lang3.ClassUtils; 096import org.apache.commons.logging.Log; 097 098/** 099 * Provides access to the page container elements.<p> 100 * 101 * @since 8.0 102 */ 103public class CmsJspTagContainer extends BodyTagSupport implements TryCatchFinally, I_CmsJspTagParamParent { 104 105 /** Default number of max elements in the container in case no value has been set. */ 106 public static final String DEFAULT_MAX_ELEMENTS = "100"; 107 108 /** The detail function container name. */ 109 public static final String DETAIL_FUNCTION_CONTAINER_NAME = "FunctionDefault"; 110 111 /** HTML used for invisible dummy elements. */ 112 public static final String DUMMY_ELEMENT = "<div class='" 113 + CmsTemplateContextInfo.DUMMY_ELEMENT_MARKER 114 + "' style='display: none !important;'></div>"; 115 116 /** The default tag name constant. */ 117 private static final String DEFAULT_TAG_NAME = "div"; 118 119 /** The log object for this class. */ 120 private static final Log LOG = CmsLog.getLog(CmsJspTagContainer.class); 121 122 /** Serial version UID required for safe serialization. */ 123 private static final long serialVersionUID = -1228397990961282556L; 124 125 /** The evaluated body content if available. */ 126 private String m_bodyContent; 127 128 /** If false, formatters are always included in non-cacheable mode, otherwise they are included in cacheable mode in the Online project only. */ 129 private boolean m_cacheable = true; 130 131 /** States if this container should only be displayed on detail pages. */ 132 private boolean m_detailOnly; 133 134 /** The detail-view attribute value. */ 135 private boolean m_detailView; 136 137 /** The editable by tag attribute. A comma separated list of OpenCms principals. */ 138 private String m_editableBy; 139 140 /** Indicating that the container page editor is active for the current request. */ 141 private boolean m_editableRequest; 142 143 /** Indicates this container is nested within a model group, only set for editable requests. */ 144 private boolean m_hasModelGroupAncestor; 145 146 /** The maxElements attribute value. */ 147 private String m_maxElements; 148 149 /** The name attribute value. */ 150 private String m_name; 151 152 /** 153 * The container name prefix to use for nested container names. 154 * If empty the element instance id of the parent element will be used. 155 **/ 156 private String m_namePrefix; 157 158 /** The optional container parameter. */ 159 private String m_param; 160 161 /** The parameter state. */ 162 private CmsJspTagAddParams.ParamState m_paramState; 163 164 /** The parent container. */ 165 private CmsContainerBean m_parentContainer; 166 167 /** The parent element to this container. */ 168 private CmsContainerElementBean m_parentElement; 169 170 /** The container setting presets. */ 171 private HashMap<String, String> m_settingPresets; 172 173 /** The tag attribute value. */ 174 private String m_tag; 175 176 /** The class attribute value. */ 177 private String m_tagClass; 178 179 /** The type attribute value. */ 180 private String m_type; 181 182 /** The container width as a string. */ 183 private String m_width; 184 185 /** 186 * Ensures the appropriate formatter configuration ID is set in the element settings.<p> 187 * 188 * @param cms the cms context 189 * @param element the element bean 190 * @param adeConfig the ADE configuration data 191 * @param containerName the container name 192 * @param containerType the container type 193 * @param containerWidth the container width 194 * 195 * @return the formatter configuration bean, may be <code>null</code> if no formatter available or a schema formatter is used 196 */ 197 public static I_CmsFormatterBean ensureValidFormatterSettings( 198 CmsObject cms, 199 CmsContainerElementBean element, 200 CmsADEConfigData adeConfig, 201 String containerName, 202 String containerType, 203 int containerWidth) { 204 205 I_CmsFormatterBean formatterBean = getFormatterConfigurationForElement( 206 cms, 207 element, 208 adeConfig, 209 containerName, 210 containerType, 211 containerWidth); 212 String settingsKey = CmsFormatterConfig.getSettingsKeyForContainer(containerName); 213 if (formatterBean != null) { 214 String keyOrId = formatterBean.getKeyOrId(); 215 if (keyOrId == null) { 216 keyOrId = CmsFormatterConfig.SCHEMA_FORMATTER_ID + formatterBean.getJspStructureId().toString(); 217 } 218 element.getSettings().put(settingsKey, keyOrId); 219 element.setFormatterId(formatterBean.getJspStructureId()); 220 } 221 return formatterBean; 222 } 223 224 /** 225 * Returns the formatter configuration for the given element.<p> 226 * 227 * @param cms the cms context 228 * @param element the element bean 229 * @param adeConfig the ADE configuration 230 * @param containerName the container name 231 * @param containerType the container type 232 * @param containerWidth the container width 233 * 234 * @return the formatter configuration 235 */ 236 public static I_CmsFormatterBean getFormatterConfigurationForElement( 237 CmsObject cms, 238 CmsContainerElementBean element, 239 CmsADEConfigData adeConfig, 240 String containerName, 241 String containerType, 242 int containerWidth) { 243 244 I_CmsFormatterBean formatterBean = null; 245 String settingsKey = CmsFormatterConfig.getSettingsKeyForContainer(containerName); 246 String formatterSetting = element.getSettings().get(settingsKey); 247 CmsFormatterConfiguration formatterConfig = null; 248 if (formatterSetting != null) { 249 formatterConfig = adeConfig.getFormatters(cms, element.getResource()); 250 // getFormattersForKey also works for the schema_formaterXXXXXX setting values 251 List<I_CmsFormatterBean> candidates = formatterConfig.getFormattersForKey(formatterSetting); 252 if (candidates.size() > 0) { 253 formatterBean = candidates.get(0); 254 } else { 255 formatterBean = adeConfig.findFormatter(formatterSetting); 256 } 257 } 258 259 if ((formatterBean == null) && (element.getFormatterId() != null) && !element.getFormatterId().isNullUUID()) { 260 if (formatterConfig == null) { 261 formatterConfig = adeConfig.getFormatters(cms, element.getResource()); 262 } 263 264 for (I_CmsFormatterBean formatter : adeConfig.getFormatters( 265 cms, 266 element.getResource()).getAllMatchingFormatters(containerType, containerWidth)) { 267 268 if (element.getFormatterId().equals(formatter.getJspStructureId())) { 269 String formatterConfigId = formatter.getId(); 270 if (formatterConfigId == null) { 271 formatterConfigId = CmsFormatterConfig.SCHEMA_FORMATTER_ID 272 + element.getFormatterId().toString(); 273 } 274 formatterBean = formatter; 275 break; 276 } 277 } 278 } 279 280 if (formatterBean == null) { 281 282 formatterBean = adeConfig.getFormatters(cms, element.getResource()).getDefaultFormatter( 283 containerType, 284 containerWidth); 285 } 286 return formatterBean; 287 } 288 289 /** 290 * Returns the element group elements.<p> 291 * 292 * @param cms the current cms context 293 * @param element group element 294 * @param req the servlet request 295 * @param containerType the container type 296 * 297 * @return the elements of this group 298 * 299 * @throws CmsException if something goes wrong 300 */ 301 public static List<CmsContainerElementBean> getGroupContainerElements( 302 CmsObject cms, 303 CmsContainerElementBean element, 304 ServletRequest req, 305 String containerType) 306 throws CmsException { 307 308 List<CmsContainerElementBean> subElements; 309 CmsXmlGroupContainer xmlGroupContainer = CmsXmlGroupContainerFactory.unmarshal(cms, element.getResource(), req); 310 CmsGroupContainerBean groupContainer = xmlGroupContainer.getGroupContainer(cms); 311 groupContainer = CmsTemplateMapper.get(req).transformGroupContainer( 312 cms, 313 groupContainer, 314 xmlGroupContainer.getFile().getRootPath()); 315 if (!CmsElementUtil.checkGroupAllowed(containerType, groupContainer)) { 316 LOG.warn( 317 new CmsIllegalStateException( 318 Messages.get().container( 319 Messages.ERR_XSD_NO_TEMPLATE_FORMATTER_3, 320 element.getResource().getRootPath(), 321 OpenCms.getResourceManager().getResourceType(element.getResource()).getTypeName(), 322 containerType))); 323 return Collections.emptyList(); 324 } 325 subElements = groupContainer.getElements(); 326 return subElements; 327 } 328 329 /** 330 * Reads elements from an inherited container.<p> 331 * 332 * @param cms the current CMS context 333 * @param element the element which references the inherited container 334 * 335 * @return the container elements 336 */ 337 338 public static List<CmsContainerElementBean> getInheritedContainerElements( 339 CmsObject cms, 340 CmsContainerElementBean element) { 341 342 CmsResource resource = element.getResource(); 343 return CmsXmlInheritGroupContainerHandler.loadInheritContainerElements(cms, resource); 344 } 345 346 /** 347 * Returns the prefixed nested container name.<p> 348 * This will be either {parentInstanceId}-{name} or {namePrefix}-{name} or in case namePrefix equals 'none' {name} only.<p> 349 * 350 * @param name the container name 351 * @param parentIstanceId the parent instance id 352 * @param namePrefix the name prefix attribute 353 * 354 * @return the nested container name 355 */ 356 public static String getNestedContainerName(String name, String parentIstanceId, String namePrefix) { 357 358 String prefix; 359 if (CmsStringUtil.isEmptyOrWhitespaceOnly(namePrefix)) { 360 prefix = parentIstanceId + "-"; 361 } else if ("none".equals(namePrefix)) { 362 prefix = ""; 363 } else { 364 prefix = namePrefix + "-"; 365 } 366 return prefix + name; 367 } 368 369 /** 370 * Creates the closing tag for the container.<p> 371 * 372 * @param tagName the tag name 373 * 374 * @return the closing tag 375 */ 376 protected static String getTagClose(String tagName) { 377 378 return "</" + tagName + ">"; 379 } 380 381 /** 382 * Creates the opening tag for the container assigning the appropriate id and class attributes.<p> 383 * 384 * @param tagName the tag name 385 * @param containerName the container name used as id attribute value 386 * @param tagClass the tag class attribute value 387 * @param nested true if this is a nested container 388 * @param online true if we are in the online project 389 * @param containerData the container data 390 * 391 * @return the opening tag 392 */ 393 protected static String getTagOpen( 394 String tagName, 395 String containerName, 396 String tagClass, 397 boolean nested, 398 boolean online, 399 String containerData) { 400 401 StringBuffer buffer = new StringBuffer(32); 402 buffer.append("<").append(tagName).append(" "); 403 if (online && nested) { 404 // omit generated ids when online 405 } else { 406 buffer.append(" id=\"").append(containerName).append("\" "); 407 } 408 if (containerData != null) { 409 buffer.append(" " + CmsGwtConstants.ATTR_DATA_CONTAINER + "=\"").append(containerData).append("\" "); 410 // set the marker CSS class 411 tagClass = tagClass == null 412 ? CmsContainerElement.CLASS_CONTAINER 413 : tagClass + " " + CmsContainerElement.CLASS_CONTAINER; 414 } 415 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(tagClass)) { 416 buffer.append("class=\"").append(tagClass).append("\" "); 417 } 418 buffer.append(">"); 419 return buffer.toString(); 420 } 421 422 /** 423 * @see org.opencms.jsp.I_CmsJspTagParamParent#addParameter(java.lang.String, java.lang.String) 424 */ 425 public void addParameter(String name, String value) { 426 427 if (m_paramState != null) { 428 m_paramState.addParameter(name, value); 429 } 430 } 431 432 /** 433 * @see javax.servlet.jsp.tagext.BodyTagSupport#doAfterBody() 434 */ 435 @SuppressWarnings("resource") 436 @Override 437 public int doAfterBody() { 438 439 // store the evaluated body content for later use 440 BodyContent bc = getBodyContent(); 441 if (bc != null) { 442 m_bodyContent = bc.getString(); 443 try { 444 bc.clear(); 445 } catch (IOException e) { 446 LOG.error(e.getLocalizedMessage(), e); 447 } 448 } 449 return SKIP_BODY; 450 } 451 452 /** 453 * @see javax.servlet.jsp.tagext.TryCatchFinally#doCatch(java.lang.Throwable) 454 */ 455 public void doCatch(Throwable t) throws Throwable { 456 457 throw t; 458 } 459 460 /** 461 * @see javax.servlet.jsp.tagext.TagSupport#doEndTag() 462 */ 463 @Override 464 public int doEndTag() throws JspException { 465 466 ServletRequest req = pageContext.getRequest(); 467 // This will always be true if the page is called through OpenCms 468 if (CmsFlexController.isCmsRequest(req)) { 469 470 try { 471 CmsFlexController controller = CmsFlexController.getController(req); 472 CmsObject cms = controller.getCmsObject(); 473 String requestUri = cms.getRequestContext().getUri(); 474 Locale locale = cms.getRequestContext().getLocale(); 475 CmsJspStandardContextBean standardContext = CmsJspStandardContextBean.getInstance(req); 476 standardContext.initPage(); 477 m_editableRequest = standardContext.getIsEditMode(); 478 m_parentElement = standardContext.getElement(); 479 m_parentContainer = standardContext.getContainer(); 480 m_hasModelGroupAncestor = m_editableRequest ? hasModelGroupAncestor(standardContext) : false; 481 CmsContainerPageBean containerPage = standardContext.getPage(); 482 CmsResource detailContent = standardContext.getDetailContent(); 483 CmsResource detailFunctionPage = standardContext.getDetailFunctionPage(); 484 // get the container 485 CmsContainerBean container = null; 486 boolean detailOnly = m_detailOnly || ((m_parentContainer != null) && m_parentContainer.isDetailOnly()); 487 if (detailOnly) { 488 if (detailContent == null) { 489 // this is no detail page, so the detail only container will not be rendered at all 490 resetState(); 491 return EVAL_PAGE; 492 } else { 493 String pageRootPath = cms.getRequestContext().addSiteRoot(cms.getRequestContext().getUri()); 494 CmsContainerPageBean detailOnlyPage = CmsDetailOnlyContainerUtil.getDetailOnlyPage( 495 cms, 496 req, 497 pageRootPath); 498 if (detailOnlyPage != null) { 499 container = detailOnlyPage.getContainers().get(getName()); 500 } 501 if ((container == null) && m_editableRequest && (containerPage != null)) { 502 // this is for the case where the current container is the nested container of a model group which the user is dragging into a detail container 503 container = containerPage.getContainers().get(getName()); 504 } 505 } 506 } else if (containerPage != null) { 507 container = containerPage.getContainers().get(getName()); 508 } 509 // get the maximal number of elements 510 int maxElements = getMaxElements(requestUri); 511 if (container == null) { 512 container = new CmsContainerBean( 513 getName(), 514 getType(), 515 m_parentElement != null ? m_parentElement.getInstanceId() : null, 516 (m_parentContainer == null) || (m_detailOnly && !m_parentContainer.isDetailOnly()), 517 maxElements, 518 Collections.<CmsContainerElementBean> emptyList()); 519 } else if ((m_parentElement != null) 520 && !m_detailOnly //ignore parent information for detail only containers to render content on different detail pages. 521 && !m_parentElement.getInstanceId().equals(container.getParentInstanceId())) { 522 // the container parent instance id does not match the parent element instance id, skip rendering to avoid recursion 523 LOG.error( 524 new CmsIllegalStateException( 525 Messages.get().container( 526 Messages.ERR_INVALID_CONTAINER_PARENT_2, 527 getName(), 528 m_parentElement.getInstanceId()))); 529 resetState(); 530 return EVAL_PAGE; 531 } 532 // set the parameter 533 container.setParam(getParam()); 534 // set the detail only flag 535 container.setDetailOnly(detailOnly); 536 boolean isUsedAsDetailView = false; 537 if (m_detailView && ((detailContent != null) || (detailFunctionPage != null))) { 538 isUsedAsDetailView = true; 539 } 540 // create tag for container 541 String tagName = CmsStringUtil.isEmptyOrWhitespaceOnly(getTag()) ? DEFAULT_TAG_NAME : getTag(); 542 pageContext.getOut().print( 543 getTagOpen( 544 tagName, 545 getName(), 546 getTagClass(), 547 isNested(), 548 !m_editableRequest, 549 m_editableRequest ? getContainerData(cms, maxElements, isUsedAsDetailView, detailOnly) : null)); 550 551 standardContext.setContainer(container); 552 // validate the type 553 if (!getType().equals(container.getType())) { 554 container.setType(getType()); 555 LOG.warn( 556 new CmsIllegalStateException( 557 Messages.get().container( 558 Messages.LOG_WRONG_CONTAINER_TYPE_4, 559 new Object[] {requestUri, locale, getName(), getType()}))); 560 } 561 562 // update the cache 563 container.setMaxElements(maxElements); 564 container.setWidth("" + getContainerWidth()); 565 List<CmsContainerElementBean> allElements = new ArrayList<CmsContainerElementBean>(); 566 CmsContainerElementBean detailElement = null; 567 if (isUsedAsDetailView) { 568 if (detailContent != null) { 569 detailElement = generateDetailViewElement(req, cms, detailContent, container); 570 } else { 571 detailElement = getDetailFunctionElement(cms, detailFunctionPage, req); 572 } 573 } 574 if (detailElement != null) { 575 allElements.add(detailElement); 576 } else { 577 allElements.addAll(container.getElements()); 578 } 579 // iterate over elements to render 580 int numRenderedElements = 0; 581 boolean first = true; 582 for (CmsContainerElementBean elementBean : allElements) { 583 // in case of rendering a detail container on a detail page, 584 // the first element may be used to provide settings for the detail content 585 // this element will not be rendered, in case the detail page is not actually used to render detail content 586 boolean skipDetailTemplateElement = false; 587 try { 588 skipDetailTemplateElement = first 589 && !m_editableRequest 590 && m_detailView 591 && (detailElement == null) 592 && OpenCms.getADEManager().isDetailPage(cms, standardContext.getPageResource()) 593 && OpenCms.getADEManager().getDetailPages(cms, elementBean.getTypeName()).contains( 594 CmsResource.getFolderPath(standardContext.getPageResource().getRootPath())); 595 } catch (Exception e) { 596 LOG.error(e.getLocalizedMessage(), e); 597 } 598 first = false; 599 if (!skipDetailTemplateElement) { 600 try { 601 boolean rendered = renderContainerElement( 602 (HttpServletRequest)req, 603 cms, 604 standardContext, 605 elementBean, 606 locale, 607 numRenderedElements >= maxElements); 608 if (rendered) { 609 numRenderedElements += 1; 610 } 611 } catch (Exception e) { 612 if (LOG.isErrorEnabled()) { 613 LOG.error(e.getLocalizedMessage(), e); 614 } 615 } 616 } 617 } 618 if ((numRenderedElements == 0) && (m_bodyContent != null) && CmsJspTagEditable.isEditableRequest(req)) { 619 // the container is empty, print the evaluated body content 620 pageContext.getOut().print(m_bodyContent); 621 } 622 // close tag for container 623 pageContext.getOut().print(getTagClose(tagName)); 624 } catch (Exception ex) { 625 if (LOG.isErrorEnabled()) { 626 LOG.error(Messages.get().getBundle().key(Messages.ERR_PROCESS_TAG_1, "container"), ex); 627 } 628 throw new javax.servlet.jsp.JspException(ex); 629 } 630 } 631 632 resetState(); 633 return super.doEndTag(); 634 } 635 636 /** 637 * @see javax.servlet.jsp.tagext.TryCatchFinally#doFinally() 638 */ 639 public void doFinally() { 640 641 if (m_paramState != null) { 642 m_paramState.undoChanges(); 643 m_paramState = null; 644 } 645 } 646 647 /** 648 * Internal action method.<p> 649 * 650 * @return EVAL_BODY_BUFFERED 651 * 652 * @see javax.servlet.jsp.tagext.Tag#doStartTag() 653 */ 654 @Override 655 public int doStartTag() { 656 657 if (CmsFlexController.isCmsRequest(pageContext.getRequest())) { 658 m_paramState = new ParamState( 659 CmsFlexController.getController(pageContext.getRequest()).getCurrentRequest()); 660 m_paramState.init(); 661 } 662 return EVAL_BODY_BUFFERED; 663 } 664 665 /** 666 * Returns the boolean value if this container is target of detail views.<p> 667 * 668 * @return <code>true</code> or <code>false</code> 669 */ 670 public String getDetailview() { 671 672 return String.valueOf(m_detailView); 673 } 674 675 /** 676 * Returns the editable by tag attribute.<p> 677 * 678 * @return the editable by tag attribute 679 */ 680 public String getEditableby() { 681 682 return m_editableBy; 683 } 684 685 /** 686 * Returns the maxElements attribute value.<p> 687 * 688 * @return the maxElements attribute value 689 */ 690 public String getMaxElements() { 691 692 return CmsStringUtil.isEmptyOrWhitespaceOnly(m_maxElements) ? DEFAULT_MAX_ELEMENTS : m_maxElements; 693 } 694 695 /** 696 * Returns the container name, in case of nested containers with a prefix to guaranty uniqueness.<p> 697 * 698 * @return String the container name 699 */ 700 public String getName() { 701 702 if (isNested()) { 703 return getNestedContainerName(m_name, m_parentElement.getInstanceId(), m_namePrefix); 704 } 705 return m_name; 706 } 707 708 /** 709 * Returns the name prefix.<p> 710 * 711 * @return the namePrefix 712 */ 713 public String getNameprefix() { 714 715 return m_namePrefix; 716 } 717 718 /** 719 * Returns the (optional) container parameter.<p> 720 * 721 * This is useful for a dynamically generated nested container, 722 * to pass information to the formatter used inside that container. 723 * 724 * If no parameters have been set, this will return <code>null</code> 725 * 726 * @return the (optional) container parameter 727 */ 728 public String getParam() { 729 730 return m_param; 731 } 732 733 /** 734 * Returns the tag attribute.<p> 735 * 736 * @return the tag attribute 737 */ 738 public String getTag() { 739 740 return m_tag; 741 } 742 743 /** 744 * Returns the tag class attribute.<p> 745 * 746 * @return the tag class attribute 747 */ 748 public String getTagClass() { 749 750 return m_tagClass; 751 } 752 753 /** 754 * Returns the type attribute value.<p> 755 * 756 * If the container type has not been set, the name is substituted as type.<p> 757 * 758 * @return the type attribute value 759 */ 760 public String getType() { 761 762 return CmsStringUtil.isEmptyOrWhitespaceOnly(m_type) ? getName() : m_type; 763 } 764 765 /** 766 * Returns the container width as a string.<p> 767 * 768 * @return the container width as a string 769 */ 770 public String getWidth() { 771 772 return m_width; 773 } 774 775 /** 776 * Sets the 'cacheable' mode for included formatters. 777 * 778 * <p>If this is set to false, formatters will never be included in cacheable mode, otherwise they will 779 * only be included in cacheable mode in the Online project. 780 * 781 * @param cacheable the cacheable mode (true or false) 782 */ 783 public void setCacheable(String cacheable) { 784 785 m_cacheable = Boolean.parseBoolean(cacheable); 786 } 787 788 /** 789 * Sets if this container should only be displayed on detail pages.<p> 790 * 791 * @param detailOnly if this container should only be displayed on detail pages 792 */ 793 public void setDetailonly(String detailOnly) { 794 795 m_detailOnly = Boolean.parseBoolean(detailOnly); 796 } 797 798 /** 799 * Sets if the current container is target of detail views.<p> 800 * 801 * @param detailView <code>true</code> or <code>false</code> 802 */ 803 public void setDetailview(String detailView) { 804 805 m_detailView = Boolean.parseBoolean(detailView); 806 } 807 808 /** 809 * Sets the editable by tag attribute.<p> 810 * 811 * @param editableBy the editable by tag attribute to set 812 */ 813 public void setEditableby(String editableBy) { 814 815 m_editableBy = editableBy; 816 } 817 818 /** 819 * Sets the maxElements attribute value.<p> 820 * 821 * @param maxElements the maxElements value to set 822 */ 823 public void setMaxElements(String maxElements) { 824 825 m_maxElements = maxElements; 826 } 827 828 /** 829 * Sets the name attribute value.<p> 830 * 831 * @param name the name value to set 832 */ 833 public void setName(String name) { 834 835 m_name = name; 836 } 837 838 /** 839 * Sets the name prefix.<p> 840 * 841 * @param namePrefix the name prefix to set 842 */ 843 public void setNameprefix(String namePrefix) { 844 845 m_namePrefix = namePrefix; 846 } 847 848 /** 849 * Sets the container parameter.<p> 850 * 851 * This is useful for a dynamically generated nested container, 852 * to pass information to the formatter used inside that container. 853 * 854 * @param param the parameter String to set 855 */ 856 public void setParam(String param) { 857 858 m_param = param; 859 } 860 861 /** 862 * Sets the setting presets.<p> 863 * 864 * @param presets a map with string keys and values, or null 865 */ 866 @SuppressWarnings("unchecked") 867 public void setSettings(Object presets) { 868 869 if (presets == null) { 870 m_settingPresets = null; 871 } else if (!(presets instanceof Map)) { 872 throw new IllegalArgumentException( 873 "cms:container -- value of 'settings' attribute should be a map, but is " 874 + ClassUtils.getCanonicalName(presets)); 875 } else { 876 m_settingPresets = new HashMap<>((Map<String, String>)presets); 877 } 878 } 879 880 /** 881 * Sets the tag attribute.<p> 882 * 883 * @param tag the createTag to set 884 */ 885 public void setTag(String tag) { 886 887 m_tag = tag; 888 } 889 890 /** 891 * Sets the tag class attribute.<p> 892 * 893 * @param tagClass the tag class attribute to set 894 */ 895 public void setTagClass(String tagClass) { 896 897 m_tagClass = tagClass; 898 } 899 900 /** 901 * Sets the type attribute value.<p> 902 * 903 * @param type the type value to set 904 */ 905 public void setType(String type) { 906 907 m_type = type; 908 } 909 910 /** 911 * Sets the container width as a string.<p> 912 * 913 * @param width the container width as a string 914 */ 915 public void setWidth(String width) { 916 917 m_width = width; 918 } 919 920 /** 921 * Returns the serialized data of the given container.<p> 922 * 923 * @param cms the cms context 924 * @param maxElements the maximum number of elements allowed within this container 925 * @param isDetailView <code>true</code> if this container is currently being used for the detail view 926 * @param isDetailOnly <code>true</code> if this is a detail only container 927 * 928 * @return the serialized container data 929 */ 930 protected String getContainerData(CmsObject cms, int maxElements, boolean isDetailView, boolean isDetailOnly) { 931 932 int width = -1; 933 try { 934 if (getWidth() != null) { 935 width = Integer.parseInt(getWidth()); 936 } 937 } catch (NumberFormatException e) { 938 //ignore; set width to -1 939 LOG.debug("Error parsing container width.", e); 940 } 941 CmsContainer cont = new CmsContainer( 942 getName(), 943 getType(), 944 m_bodyContent, 945 width, 946 maxElements, 947 m_detailView, 948 isDetailView, 949 !m_hasModelGroupAncestor && isEditable(cms), 950 null, 951 m_parentContainer != null ? m_parentContainer.getName() : null, 952 m_parentElement != null ? m_parentElement.getInstanceId() : null, 953 m_settingPresets); 954 cont.setDetailOnly(isDetailOnly); 955 String result = ""; 956 try { 957 result = CmsContainerpageService.getSerializedContainerInfo(cont); 958 } catch (Exception e) { 959 LOG.error(e.getLocalizedMessage(), e); 960 } 961 962 return result; 963 } 964 965 /** 966 * Returns if the container is editable by the current user.<p> 967 * 968 * @param cms the cms context 969 * 970 * @return <code>true</code> if the container is editable by the current user 971 */ 972 protected boolean isEditable(CmsObject cms) { 973 974 boolean result = false; 975 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_editableBy)) { 976 String[] principals = m_editableBy.split(","); 977 List<CmsGroup> groups = null; 978 for (int i = 0; i < principals.length; i++) { 979 String key = principals[i]; 980 // get the principal name from the principal String 981 String principal = key.substring(key.indexOf('.') + 1, key.length()); 982 983 if (CmsGroup.hasPrefix(key)) { 984 // read the group 985 principal = OpenCms.getImportExportManager().translateGroup(principal); 986 try { 987 CmsGroup group = cms.readGroup(principal); 988 if (groups == null) { 989 try { 990 groups = cms.getGroupsOfUser(cms.getRequestContext().getCurrentUser().getName(), false); 991 } catch (Exception ex) { 992 if (LOG.isErrorEnabled()) { 993 LOG.error(ex.getLocalizedMessage(), ex); 994 } 995 groups = Collections.emptyList(); 996 } 997 } 998 result = groups.contains(group); 999 } catch (CmsException e) { 1000 if (LOG.isErrorEnabled()) { 1001 LOG.error(e.getLocalizedMessage(), e); 1002 } 1003 } 1004 } else if (CmsUser.hasPrefix(key)) { 1005 // read the user 1006 principal = OpenCms.getImportExportManager().translateUser(principal); 1007 try { 1008 result = cms.getRequestContext().getCurrentUser().equals(cms.readUser(principal)); 1009 } catch (CmsException e) { 1010 if (LOG.isErrorEnabled()) { 1011 LOG.error(e.getLocalizedMessage(), e); 1012 } 1013 } 1014 } else if (CmsRole.hasPrefix(key)) { 1015 // read the role with role name 1016 CmsRole role = CmsRole.valueOfRoleName(principal); 1017 if (role == null) { 1018 // try to read the role in the old fashion with group name 1019 role = CmsRole.valueOfGroupName(principal); 1020 } 1021 if (role != null) { 1022 result = OpenCms.getRoleManager().hasRole( 1023 cms, 1024 role.forOrgUnit(cms.getRequestContext().getCurrentUser().getOuFqn())); 1025 } 1026 } 1027 if (result) { 1028 break; 1029 } 1030 } 1031 } else { 1032 result = OpenCms.getRoleManager().hasRole(cms, CmsRole.ELEMENT_AUTHOR); 1033 } 1034 return result; 1035 } 1036 1037 /** 1038 * Returns true if this is a nested container.<p> 1039 * 1040 * @return true if this is a nested container 1041 */ 1042 protected boolean isNested() { 1043 1044 return (m_parentContainer != null) && (m_parentElement != null); 1045 } 1046 1047 /** 1048 * Prints the closing tag for an element wrapper if in online mode.<p> 1049 * 1050 * @param isGroupcontainer <code>true</code> if element is a group-container 1051 * 1052 * @throws IOException if the output fails 1053 */ 1054 protected void printElementWrapperTagEnd(boolean isGroupcontainer) throws IOException { 1055 1056 if (m_editableRequest) { 1057 String result; 1058 if (isGroupcontainer) { 1059 result = "</div>"; 1060 } else { 1061 result = "<div class=\"" 1062 + CmsContainerElement.CLASS_CONTAINER_ELEMENT_END_MARKER 1063 + "\" style=\"display:none\"></div>"; 1064 } 1065 pageContext.getOut().print(result); 1066 } 1067 } 1068 1069 /** 1070 * Prints the opening element wrapper tag for the container page editor if we are in Offline mode.<p> 1071 * 1072 * @param cms the Cms context 1073 * @param elementBean the element bean 1074 * @param page the container page 1075 * @param isGroupContainer true if the element is a group-container 1076 * 1077 * @throws Exception if something goes wrong 1078 */ 1079 protected void printElementWrapperTagStart( 1080 CmsObject cms, 1081 CmsContainerElementBean elementBean, 1082 CmsContainerPageBean page, 1083 boolean isGroupContainer) 1084 throws Exception { 1085 1086 if (m_editableRequest) { 1087 StringBuffer result = new StringBuffer("<div class='"); 1088 if (isGroupContainer) { 1089 result.append(CmsContainerElement.CLASS_GROUP_CONTAINER_ELEMENT_MARKER); 1090 } else { 1091 result.append(CmsContainerElement.CLASS_CONTAINER_ELEMENT_START_MARKER); 1092 } 1093 String serializedElement = getElementInfo(cms, elementBean, page); 1094 result.append("'"); 1095 result.append(" " + CmsGwtConstants.ATTR_DATA_ELEMENT + "='").append(serializedElement); 1096 if (isGroupContainer) { 1097 result.append("'>"); 1098 } else { 1099 result.append("' style='display:none;'></div>"); 1100 } 1101 pageContext.getOut().print(result); 1102 } 1103 } 1104 1105 /** 1106 * Generates the detail view element.<p> 1107 * 1108 * @param request the current request 1109 * @param cms the CMS context 1110 * @param detailContent the detail content resource 1111 * @param container the container 1112 * 1113 * @return the detail view element 1114 */ 1115 private CmsContainerElementBean generateDetailViewElement( 1116 ServletRequest request, 1117 CmsObject cms, 1118 CmsResource detailContent, 1119 CmsContainerBean container) { 1120 1121 CmsContainerElementBean element = null; 1122 if (detailContent != null) { 1123 // get the right formatter 1124 1125 CmsADEConfigData config = OpenCms.getADEManager().lookupConfigurationWithCache( 1126 cms, 1127 cms.getRequestContext().getRootUri()); 1128 CmsFormatterConfiguration formatters = config.getFormatters(cms, detailContent); 1129 I_CmsFormatterBean formatter = formatters.getDetailFormatter(getType(), getContainerWidth()); 1130 1131 if (formatter != null) { 1132 // use structure id as the instance id to enable use of nested containers 1133 Map<String, String> settings = new HashMap<String, String>(); 1134 for (CmsContainerElementBean el : container.getElements()) { 1135 try { 1136 el.initResource(cms); 1137 if (el.getResource().getTypeId() == detailContent.getTypeId()) { 1138 settings.putAll(el.getIndividualSettings()); 1139 break; 1140 } 1141 } catch (CmsException e) { 1142 LOG.error(e.getLocalizedMessage(), e); 1143 } 1144 } 1145 1146 String formatterKey = CmsFormatterConfig.getSettingsKeyForContainer(container.getName()); 1147 if (settings.containsKey(formatterKey)) { 1148 String formatterConfigId = settings.get(formatterKey); 1149 I_CmsFormatterBean dynamicFmt = config.findFormatter(formatterConfigId); 1150 if (dynamicFmt != null) { 1151 formatter = dynamicFmt; 1152 } 1153 } 1154 settings.put(formatterKey, formatter.getKeyOrId()); 1155 settings.put(CmsContainerElement.ELEMENT_INSTANCE_ID, new CmsUUID().toString()); 1156 // create element bean 1157 element = new CmsContainerElementBean( 1158 detailContent.getStructureId(), 1159 formatter.getJspStructureId(), 1160 settings, 1161 false); 1162 String pageRootPath = cms.getRequestContext().addSiteRoot(cms.getRequestContext().getUri()); 1163 element = CmsTemplateMapper.get(request).transformDetailElement(cms, element, pageRootPath); 1164 } 1165 } 1166 return element; 1167 } 1168 1169 /** 1170 * Gets the container width as a number.<p> 1171 * 1172 * If the container width is not set, or not a number, -1 will be returned.<p> 1173 * 1174 * @return the container width or -1 1175 */ 1176 private int getContainerWidth() { 1177 1178 int containerWidth = -1; 1179 try { 1180 containerWidth = Integer.parseInt(m_width); 1181 } catch (NumberFormatException e) { 1182 // do nothing, set width to -1 1183 LOG.debug("Error parsing container width.", e); 1184 } 1185 return containerWidth; 1186 } 1187 1188 /** 1189 * Returns the detail function element.<p> 1190 * 1191 * @param cms the cms context 1192 * @param detailFunctionPage the detail function page 1193 * @param req the current request 1194 * 1195 * @return the detail function element, if available 1196 */ 1197 private CmsContainerElementBean getDetailFunctionElement( 1198 CmsObject cms, 1199 CmsResource detailFunctionPage, 1200 ServletRequest req) { 1201 1202 try { 1203 CmsXmlContainerPage xmlContainerPage = CmsXmlContainerPageFactory.unmarshal(cms, detailFunctionPage, req); 1204 1205 CmsContainerPageBean page = xmlContainerPage.getContainerPage(cms); 1206 CmsContainerBean container = page.getContainers().get(DETAIL_FUNCTION_CONTAINER_NAME); 1207 if (container == null) { 1208 for (Entry<String, CmsContainerBean> entry : page.getContainers().entrySet()) { 1209 if (entry.getKey().endsWith("-" + DETAIL_FUNCTION_CONTAINER_NAME)) { 1210 container = entry.getValue(); 1211 break; 1212 } 1213 } 1214 } 1215 if (container != null) { 1216 return container.getElements().get(0); 1217 } 1218 } catch (CmsException e) { 1219 LOG.error(e.getLocalizedMessage(), e); 1220 } 1221 return null; 1222 } 1223 1224 /** 1225 * Returns the serialized element data.<p> 1226 * 1227 * @param cms the current cms context 1228 * @param elementBean the element to serialize 1229 * @param page the container page 1230 * 1231 * @return the serialized element data 1232 * 1233 * @throws Exception if something goes wrong 1234 */ 1235 private String getElementInfo(CmsObject cms, CmsContainerElementBean elementBean, CmsContainerPageBean page) 1236 throws Exception { 1237 1238 return CmsContainerpageService.getSerializedElementInfo( 1239 cms, 1240 (HttpServletRequest)pageContext.getRequest(), 1241 (HttpServletResponse)pageContext.getResponse(), 1242 elementBean, 1243 page); 1244 } 1245 1246 /** 1247 * Parses the maximum element number from the current container and returns the resulting number.<p> 1248 * 1249 * @param requestUri the requested URI 1250 * 1251 * @return the maximum number of elements of the container 1252 */ 1253 private int getMaxElements(String requestUri) { 1254 1255 String containerMaxElements = getMaxElements(); 1256 1257 int maxElements = -1; 1258 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(containerMaxElements)) { 1259 try { 1260 maxElements = Integer.parseInt(containerMaxElements); 1261 } catch (NumberFormatException e) { 1262 throw new CmsIllegalStateException( 1263 Messages.get().container( 1264 Messages.LOG_WRONG_CONTAINER_MAXELEMENTS_3, 1265 new Object[] {requestUri, getName(), containerMaxElements}), 1266 e); 1267 } 1268 } else { 1269 if (LOG.isWarnEnabled()) { 1270 LOG.warn( 1271 Messages.get().getBundle().key( 1272 Messages.LOG_MAXELEMENTS_NOT_SET_2, 1273 new Object[] {getName(), requestUri})); 1274 } 1275 } 1276 return maxElements; 1277 } 1278 1279 /** 1280 * Returns the ADE session cache for container elements in case of an editable request, otherwise <code>null</code>.<p> 1281 * 1282 * @param cms the cms context 1283 * 1284 * @return the session cache 1285 */ 1286 private CmsADESessionCache getSessionCache(CmsObject cms) { 1287 1288 return m_editableRequest 1289 ? CmsADESessionCache.getCache((HttpServletRequest)(pageContext.getRequest()), cms) 1290 : null; 1291 } 1292 1293 /** 1294 * Evaluates if this container is nested within a model group.<p> 1295 * 1296 * @param standardContext the standard context 1297 * 1298 * @return <code>true</code> if the container has model group ancestors 1299 */ 1300 private boolean hasModelGroupAncestor(CmsJspStandardContextBean standardContext) { 1301 1302 boolean result = false; 1303 if (!standardContext.isModelGroupPage()) { 1304 CmsContainerElementWrapper parent = standardContext.getElement(); 1305 while ((parent != null) && !result) { 1306 result = parent.isModelGroup(); 1307 parent = parent.getParent(); 1308 } 1309 } 1310 return result; 1311 } 1312 1313 /** 1314 * Prints an element error tag to the response out.<p> 1315 * 1316 * @param elementSitePath the element site path 1317 * @param formatterSitePath the formatter site path 1318 * @param exception the exception causing the error 1319 * 1320 * @throws IOException if something goes wrong writing to response out 1321 */ 1322 private void printElementErrorTag(String elementSitePath, String formatterSitePath, Exception exception) 1323 throws IOException { 1324 1325 if (m_editableRequest) { 1326 String stacktrace = CmsException.getStackTraceAsString(exception); 1327 if (CmsStringUtil.isEmptyOrWhitespaceOnly(stacktrace)) { 1328 stacktrace = null; 1329 } else { 1330 // stacktrace = CmsStringUtil.escapeJavaScript(stacktrace); 1331 stacktrace = CmsEncoder.escapeXml(stacktrace); 1332 } 1333 StringBuffer errorBox = new StringBuffer(256); 1334 errorBox.append( 1335 "<div style=\"display:block; padding: 5px; border: red solid 2px; color: black; background: white;\" class=\""); 1336 errorBox.append(CmsContainerElement.CLASS_ELEMENT_ERROR); 1337 errorBox.append("\">"); 1338 errorBox.append( 1339 Messages.get().getBundle().key( 1340 Messages.ERR_CONTAINER_PAGE_ELEMENT_RENDER_ERROR_2, 1341 elementSitePath, 1342 formatterSitePath)); 1343 errorBox.append("<br />"); 1344 errorBox.append(exception.getLocalizedMessage()); 1345 if (stacktrace != null) { 1346 errorBox.append( 1347 "<span onclick=\"opencms.openStacktraceDialog(event);\" style=\"border: 1px solid black; cursor: pointer;\">"); 1348 errorBox.append(Messages.get().getBundle().key(Messages.GUI_LABEL_STACKTRACE_0)); 1349 String title = Messages.get().getBundle().key( 1350 Messages.ERR_CONTAINER_PAGE_ELEMENT_RENDER_ERROR_2, 1351 elementSitePath, 1352 formatterSitePath); 1353 errorBox.append("<span title=\""); 1354 errorBox.append(CmsEncoder.escapeXml(title)); 1355 errorBox.append("\" class=\"hiddenStacktrace\" style=\"display:none;\">"); 1356 errorBox.append(stacktrace); 1357 errorBox.append("</span></span>"); 1358 } 1359 errorBox.append("</div>"); 1360 pageContext.getOut().print(errorBox.toString()); 1361 } 1362 } 1363 1364 /** 1365 * Renders a container element.<p> 1366 * 1367 * @param request the current request 1368 * @param cms the CMS context 1369 * @param standardContext the current standard contxt bean 1370 * @param element the container element to render 1371 * @param locale the requested locale 1372 * @param alreadyFull if true, only render invisible elements (they don't count towards the "max elements") 1373 * 1374 * @return true if an element was rendered that counts towards the container's maximum number of elements 1375 * 1376 * @throws Exception if something goes wrong 1377 */ 1378 private boolean renderContainerElement( 1379 HttpServletRequest request, 1380 CmsObject cms, 1381 CmsJspStandardContextBean standardContext, 1382 CmsContainerElementBean element, 1383 Locale locale, 1384 boolean alreadyFull) 1385 throws Exception { 1386 1387 CmsTemplateContext context = (CmsTemplateContext)(request.getAttribute( 1388 CmsTemplateContextManager.ATTR_TEMPLATE_CONTEXT)); 1389 if ((context == null) && alreadyFull) { 1390 return false; 1391 } 1392 String contextKey = null; 1393 if (context != null) { 1394 contextKey = context.getKey(); 1395 } else { 1396 String rpcContextOverride = (String)request.getAttribute( 1397 CmsTemplateContextManager.ATTR_RPC_CONTEXT_OVERRIDE); 1398 contextKey = rpcContextOverride; 1399 } 1400 boolean ignoreTemplateContexts = false; 1401 try { 1402 I_CmsTemplateContextProvider templateProvider = null; 1403 if (context != null) { 1404 templateProvider = context.getProvider(); 1405 } 1406 if (templateProvider == null) { 1407 templateProvider = OpenCms.getTemplateContextManager().getTemplateContextProvider( 1408 cms, 1409 cms.getRequestContext().getUri()); 1410 } 1411 ignoreTemplateContexts = (templateProvider != null) && templateProvider.isIgnoreTemplateContextsSetting(); 1412 } catch (CmsException e) { 1413 LOG.info(e.getLocalizedMessage(), e); 1414 } 1415 boolean showInContext = ignoreTemplateContexts 1416 || shouldShowInContext(element, context != null ? context.getKey() : null); 1417 boolean isOnline = cms.getRequestContext().getCurrentProject().isOnlineProject(); 1418 if (!m_editableRequest && !showInContext) { 1419 return false; 1420 } 1421 try { 1422 element.initResource(cms); 1423 } catch (CmsPermissionViolationException e) { 1424 LOG.info(e.getLocalizedMessage(), e); 1425 return false; 1426 } 1427 if (!m_editableRequest && !element.isReleasedAndNotExpired()) { 1428 // do not render expired resources for the online project 1429 return false; 1430 } 1431 ServletRequest req = pageContext.getRequest(); 1432 ServletResponse res = pageContext.getResponse(); 1433 String containerType = getType(); 1434 int containerWidth = getContainerWidth(); 1435 CmsADEConfigData adeConfig = OpenCms.getADEManager().lookupConfigurationWithCache( 1436 cms, 1437 cms.getRequestContext().getRootUri()); 1438 boolean isGroupContainer = element.isGroupContainer(cms); 1439 boolean isInheritedContainer = element.isInheritedContainer(cms); 1440 I_CmsFormatterBean formatterConfig = null; 1441 if (!isGroupContainer && !isInheritedContainer) { 1442 // ensure that the formatter configuration id is added to the element settings, so it will be persisted on save 1443 formatterConfig = ensureValidFormatterSettings( 1444 cms, 1445 element, 1446 adeConfig, 1447 getName(), 1448 containerType, 1449 containerWidth); 1450 element.initSettings(cms, adeConfig, formatterConfig, locale, request, m_settingPresets); 1451 } 1452 // writing elements to the session cache to improve performance of the container-page editor in offline project 1453 if (m_editableRequest) { 1454 getSessionCache(cms).setCacheContainerElement(element.editorHash(), element); 1455 } 1456 1457 if (isGroupContainer || isInheritedContainer) { 1458 if (alreadyFull) { 1459 return false; 1460 } 1461 List<CmsContainerElementBean> subElements; 1462 if (isGroupContainer) { 1463 subElements = getGroupContainerElements(cms, element, req, containerType); 1464 } else { 1465 // inherited container case 1466 subElements = getInheritedContainerElements(cms, element); 1467 } 1468 // wrapping the elements with DIV containing initial element data. To be removed by the container-page editor 1469 printElementWrapperTagStart(cms, element, standardContext.getPage(), true); 1470 for (CmsContainerElementBean subelement : subElements) { 1471 1472 try { 1473 subelement.initResource(cms); 1474 boolean shouldShowSubElementInContext = ignoreTemplateContexts 1475 || shouldShowInContext(subelement, contextKey); 1476 if (!m_editableRequest 1477 && (!shouldShowSubElementInContext || !subelement.isReleasedAndNotExpired())) { 1478 continue; 1479 } 1480 I_CmsFormatterBean subElementFormatterConfig = ensureValidFormatterSettings( 1481 cms, 1482 subelement, 1483 adeConfig, 1484 getName(), 1485 containerType, 1486 containerWidth); 1487 subelement.initSettings( 1488 cms, 1489 adeConfig, 1490 subElementFormatterConfig, 1491 locale, 1492 request, 1493 m_settingPresets); 1494 // writing elements to the session cache to improve performance of the container-page editor 1495 if (m_editableRequest) { 1496 getSessionCache(cms).setCacheContainerElement(subelement.editorHash(), subelement); 1497 } 1498 if (subElementFormatterConfig == null) { 1499 if (LOG.isErrorEnabled()) { 1500 LOG.error( 1501 new CmsIllegalStateException( 1502 Messages.get().container( 1503 Messages.ERR_XSD_NO_TEMPLATE_FORMATTER_3, 1504 subelement.getSitePath(), 1505 OpenCms.getResourceManager().getResourceType( 1506 subelement.getResource()).getTypeName(), 1507 containerType))); 1508 } 1509 // skip this element, it has no formatter for this container type defined 1510 continue; 1511 } 1512 // execute the formatter JSP for the given element URI 1513 // wrapping the elements with DIV containing initial element data. To be removed by the container-page editor 1514 printElementWrapperTagStart(cms, subelement, standardContext.getPage(), false); 1515 standardContext.setElement(subelement); 1516 try { 1517 String formatterSitePath; 1518 try { 1519 CmsResource formatterResource = cms.readResource( 1520 subElementFormatterConfig.getJspStructureId()); 1521 formatterSitePath = cms.getSitePath(formatterResource); 1522 } catch (CmsVfsResourceNotFoundException ex) { 1523 LOG.debug("Formatter JSP not found by id, try using path.", ex); 1524 formatterSitePath = cms.getRequestContext().removeSiteRoot( 1525 subElementFormatterConfig.getJspRootPath()); 1526 } 1527 if (shouldShowSubElementInContext) { 1528 CmsJspTagInclude.includeTagAction( 1529 pageContext, 1530 formatterSitePath, 1531 null, 1532 locale, 1533 false, 1534 isOnline && m_cacheable, 1535 null, 1536 CmsRequestUtil.getAttributeMap(req), 1537 req, 1538 res); 1539 } else { 1540 pageContext.getOut().print(DUMMY_ELEMENT); 1541 } 1542 } catch (Exception e) { 1543 if (LOG.isErrorEnabled()) { 1544 if (CmsJspLoader.isJasperCompilerException(e)) { 1545 LOG.error( 1546 Messages.get().getBundle().key( 1547 Messages.ERR_CONTAINER_PAGE_ELEMENT_RENDER_ERROR_2, 1548 subelement.getSitePath(), 1549 subElementFormatterConfig) + "\n" + e.getMessage()); 1550 LOG.debug("Full stack trace for error", e); 1551 } else { 1552 LOG.error( 1553 Messages.get().getBundle().key( 1554 Messages.ERR_CONTAINER_PAGE_ELEMENT_RENDER_ERROR_2, 1555 subelement.getSitePath(), 1556 subElementFormatterConfig), 1557 e); 1558 } 1559 } 1560 printElementErrorTag(subelement.getSitePath(), subElementFormatterConfig.getJspRootPath(), e); 1561 } 1562 printElementWrapperTagEnd(false); 1563 } catch (Exception e) { 1564 if (LOG.isErrorEnabled()) { 1565 LOG.error(e.getLocalizedMessage(), e); 1566 } 1567 } 1568 } 1569 printElementWrapperTagEnd(true); 1570 return true; 1571 } else { 1572 boolean result = true; 1573 if (alreadyFull) { 1574 result = false; 1575 if (!showInContext) { 1576 printElementWrapperTagStart(cms, element, standardContext.getPage(), false); 1577 pageContext.getOut().print(DUMMY_ELEMENT); 1578 printElementWrapperTagEnd(false); 1579 } 1580 } else { 1581 String formatter = null; 1582 try { 1583 if (formatterConfig != null) { 1584 try { 1585 CmsResource formatterResource = cms.readResource(formatterConfig.getJspStructureId()); 1586 formatter = cms.getSitePath(formatterResource); 1587 } catch (CmsVfsResourceNotFoundException ex) { 1588 LOG.debug("Formatter JSP not found by id, try using path.", ex); 1589 if (cms.existsResource( 1590 cms.getRequestContext().removeSiteRoot(formatterConfig.getJspRootPath()))) { 1591 formatter = cms.getRequestContext().removeSiteRoot(formatterConfig.getJspRootPath()); 1592 } 1593 } 1594 } else { 1595 formatter = cms.getSitePath(cms.readResource(element.getFormatterId())); 1596 } 1597 } catch (CmsException e) { 1598 LOG.debug("Formatter resource can not be found, try reading it from the configuration.", e); 1599 // the formatter resource can not be found, try reading it form the configuration 1600 CmsFormatterConfiguration elementFormatters = adeConfig.getFormatters(cms, element.getResource()); 1601 I_CmsFormatterBean elementFormatterBean = elementFormatters.getDefaultFormatter( 1602 containerType, 1603 containerWidth); 1604 if (elementFormatterBean == null) { 1605 if (LOG.isErrorEnabled()) { 1606 LOG.error( 1607 new CmsIllegalStateException( 1608 Messages.get().container( 1609 Messages.ERR_XSD_NO_TEMPLATE_FORMATTER_3, 1610 element.getSitePath(), 1611 OpenCms.getResourceManager().getResourceType( 1612 element.getResource()).getTypeName(), 1613 containerType))); 1614 } 1615 // skip this element, it has no formatter for this container type defined 1616 return false; 1617 } 1618 try { 1619 CmsResource formatterResource = cms.readResource(elementFormatterBean.getJspStructureId()); 1620 formatter = cms.getSitePath(formatterResource); 1621 } catch (CmsVfsResourceNotFoundException ex) { 1622 LOG.debug("Formatter JSP not found by id, try using path.", ex); 1623 formatter = cms.getRequestContext().removeSiteRoot(elementFormatterBean.getJspRootPath()); 1624 } 1625 } 1626 1627 printElementWrapperTagStart(cms, element, standardContext.getPage(), false); 1628 standardContext.setElement(element); 1629 try { 1630 if (!showInContext) { 1631 // write invisible dummy element 1632 pageContext.getOut().print(DUMMY_ELEMENT); 1633 result = false; 1634 } else { 1635 // execute the formatter jsp for the given element uri 1636 CmsJspTagInclude.includeTagAction( 1637 pageContext, 1638 formatter, 1639 null, 1640 locale, 1641 false, 1642 isOnline && m_cacheable, 1643 null, 1644 CmsRequestUtil.getAtrributeMap(req), 1645 req, 1646 res); 1647 } 1648 } catch (Exception e) { 1649 if (LOG.isErrorEnabled()) { 1650 if (CmsJspLoader.isJasperCompilerException(e)) { 1651 LOG.error( 1652 Messages.get().getBundle().key( 1653 Messages.ERR_CONTAINER_PAGE_ELEMENT_RENDER_ERROR_2, 1654 element.getSitePath(), 1655 formatter) + "\n" + e.getMessage()); 1656 LOG.debug("Full stack trace for error", e); 1657 } else { 1658 LOG.error( 1659 Messages.get().getBundle().key( 1660 Messages.ERR_CONTAINER_PAGE_ELEMENT_RENDER_ERROR_2, 1661 element.getSitePath(), 1662 formatter), 1663 e); 1664 } 1665 } 1666 printElementErrorTag(element.getSitePath(), formatter, e); 1667 } 1668 printElementWrapperTagEnd(false); 1669 } 1670 return result; 1671 } 1672 } 1673 1674 /** 1675 * Resets the tag instance and standard context state.<p> 1676 */ 1677 private void resetState() { 1678 1679 // clear all members so the tag object may be reused 1680 m_type = null; 1681 m_name = null; 1682 m_param = null; 1683 m_maxElements = null; 1684 m_tag = null; 1685 m_tagClass = null; 1686 m_detailView = false; 1687 m_detailOnly = false; 1688 m_width = null; 1689 m_editableBy = null; 1690 m_bodyContent = null; 1691 m_hasModelGroupAncestor = false; 1692 // reset the current element 1693 CmsJspStandardContextBean cmsContext = CmsJspStandardContextBean.getInstance(pageContext.getRequest()); 1694 cmsContext.setElement(m_parentElement); 1695 cmsContext.setContainer(m_parentContainer); 1696 m_parentElement = null; 1697 m_parentContainer = null; 1698 } 1699 1700 /** 1701 * Helper method to determine whether an element should be shown in a context.<p> 1702 * 1703 * @param element the element for which the visibility should be determined 1704 * @param contextKey the key of the context for which to check 1705 * 1706 * @return true if the current context doesn't prohibit the element from being shown 1707 */ 1708 private boolean shouldShowInContext(CmsContainerElementBean element, String contextKey) { 1709 1710 if (contextKey == null) { 1711 return true; 1712 } 1713 1714 try { 1715 if ((element.getResource() != null) 1716 && !OpenCms.getTemplateContextManager().shouldShowType( 1717 contextKey, 1718 OpenCms.getResourceManager().getResourceType(element.getResource().getTypeId()).getTypeName())) { 1719 return false; 1720 } 1721 } catch (CmsLoaderException e) { 1722 // ignore and log 1723 LOG.error(e.getLocalizedMessage(), e); 1724 } 1725 Map<String, String> settings = element.getSettings(); 1726 if (settings == null) { 1727 return true; 1728 } 1729 String contextsAllowed = settings.get(CmsTemplateContextInfo.SETTING); 1730 if (contextsAllowed == null) { 1731 return true; 1732 } 1733 if (contextsAllowed.equals(CmsTemplateContextInfo.EMPTY_VALUE)) { 1734 return false; 1735 } 1736 1737 List<String> contextsAllowedList = CmsStringUtil.splitAsList(contextsAllowed, "|"); 1738 if (!contextsAllowedList.contains(contextKey)) { 1739 return false; 1740 } 1741 return true; 1742 } 1743}