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