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