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