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