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, 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.ade.containerpage; 029 030import org.opencms.ade.configuration.CmsADEConfigData; 031import org.opencms.ade.configuration.CmsElementView; 032import org.opencms.ade.configuration.CmsResourceTypeConfig; 033import org.opencms.ade.containerpage.inherited.CmsContainerConfigurationCache; 034import org.opencms.ade.containerpage.inherited.CmsInheritanceReference; 035import org.opencms.ade.containerpage.inherited.CmsInheritanceReferenceParser; 036import org.opencms.ade.containerpage.inherited.CmsInheritedContainerState; 037import org.opencms.ade.containerpage.shared.CmsContainer; 038import org.opencms.ade.containerpage.shared.CmsContainerElement; 039import org.opencms.ade.containerpage.shared.CmsContainerElement.ModelGroupState; 040import org.opencms.ade.containerpage.shared.CmsContainerElementData; 041import org.opencms.ade.containerpage.shared.CmsElementSettingsConfig; 042import org.opencms.ade.containerpage.shared.CmsFormatterConfig; 043import org.opencms.ade.containerpage.shared.CmsInheritanceInfo; 044import org.opencms.ade.detailpage.CmsDetailPageResourceHandler; 045import org.opencms.configuration.CmsConfigurationException; 046import org.opencms.db.CmsResourceState; 047import org.opencms.file.CmsFile; 048import org.opencms.file.CmsObject; 049import org.opencms.file.CmsResource; 050import org.opencms.file.CmsResourceFilter; 051import org.opencms.file.types.CmsResourceTypeXmlContainerPage; 052import org.opencms.file.types.CmsResourceTypeXmlContent; 053import org.opencms.file.types.I_CmsResourceType; 054import org.opencms.gwt.CmsIconUtil; 055import org.opencms.gwt.shared.CmsAdditionalInfoBean; 056import org.opencms.gwt.shared.CmsPermissionInfo; 057import org.opencms.i18n.CmsMessageContainer; 058import org.opencms.i18n.CmsMessages; 059import org.opencms.jsp.util.CmsJspStandardContextBean; 060import org.opencms.jsp.util.CmsJspStandardContextBean.TemplateBean; 061import org.opencms.loader.CmsTemplateContextManager; 062import org.opencms.main.CmsException; 063import org.opencms.main.CmsLog; 064import org.opencms.main.OpenCms; 065import org.opencms.relations.CmsRelation; 066import org.opencms.relations.CmsRelationFilter; 067import org.opencms.relations.CmsRelationType; 068import org.opencms.search.galleries.CmsGallerySearch; 069import org.opencms.search.galleries.CmsGallerySearchResult; 070import org.opencms.util.CmsDateUtil; 071import org.opencms.util.CmsStringUtil; 072import org.opencms.util.CmsUUID; 073import org.opencms.workplace.CmsWorkplaceMessages; 074import org.opencms.workplace.editors.CmsWorkplaceEditorManager; 075import org.opencms.workplace.editors.directedit.CmsAdvancedDirectEditProvider; 076import org.opencms.workplace.editors.directedit.CmsDirectEditMode; 077import org.opencms.workplace.editors.directedit.I_CmsDirectEditProvider; 078import org.opencms.workplace.explorer.CmsExplorerTypeSettings; 079import org.opencms.workplace.explorer.CmsResourceUtil; 080import org.opencms.xml.containerpage.CmsADESessionCache; 081import org.opencms.xml.containerpage.CmsContainerBean; 082import org.opencms.xml.containerpage.CmsContainerElementBean; 083import org.opencms.xml.containerpage.CmsContainerPageBean; 084import org.opencms.xml.containerpage.CmsFormatterConfiguration; 085import org.opencms.xml.containerpage.CmsGroupContainerBean; 086import org.opencms.xml.containerpage.CmsXmlContainerPage; 087import org.opencms.xml.containerpage.CmsXmlContainerPageFactory; 088import org.opencms.xml.containerpage.CmsXmlGroupContainer; 089import org.opencms.xml.containerpage.CmsXmlGroupContainerFactory; 090import org.opencms.xml.containerpage.I_CmsFormatterBean; 091import org.opencms.xml.content.CmsXmlContent; 092import org.opencms.xml.content.CmsXmlContentFactory; 093import org.opencms.xml.content.CmsXmlContentProperty; 094import org.opencms.xml.content.CmsXmlContentPropertyHelper; 095 096import java.io.IOException; 097import java.text.DateFormat; 098import java.util.ArrayList; 099import java.util.Collection; 100import java.util.Collections; 101import java.util.Date; 102import java.util.HashMap; 103import java.util.LinkedHashMap; 104import java.util.LinkedHashSet; 105import java.util.List; 106import java.util.Locale; 107import java.util.Map; 108import java.util.Map.Entry; 109import java.util.Set; 110import java.util.function.Function; 111import java.util.regex.Matcher; 112import java.util.regex.Pattern; 113 114import javax.servlet.ServletException; 115import javax.servlet.http.HttpServletRequest; 116import javax.servlet.http.HttpServletResponse; 117 118import org.apache.commons.logging.Log; 119 120import com.google.common.base.Supplier; 121import com.google.common.base.Suppliers; 122import com.google.common.collect.Sets; 123 124/** 125 * Utility class to generate the element data objects used within the container-page editor.<p> 126 * 127 * @since 8.0.0 128 */ 129public class CmsElementUtil { 130 131 /** The maximum number of nested container levels. */ 132 public static final int MAX_NESTING_LEVEL = 7; 133 134 /** Static reference to the log. */ 135 private static final Log LOG = CmsLog.getLog(org.opencms.ade.containerpage.CmsElementUtil.class); 136 137 /** The ADE configuration data for the current page URI. */ 138 private CmsADEConfigData m_adeConfig; 139 140 /** The cms context. */ 141 private CmsObject m_cms; 142 143 /** The current page uri. */ 144 private String m_currentPageUri; 145 146 /** The content locale. */ 147 private Locale m_locale; 148 149 /** The current container page. */ 150 private CmsResource m_page; 151 152 /** The request parameters to use while rendering the elements. */ 153 @SuppressWarnings("unused") 154 private Map<String, Object> m_parameterMap; 155 156 /** The http request. */ 157 private HttpServletRequest m_req; 158 159 /** The http response. */ 160 private HttpServletResponse m_res; 161 162 /** The standard context bean. */ 163 private CmsJspStandardContextBean m_standardContext; 164 165 /** 166 * Creates a new instance.<p> 167 * Use this constructor to set the current container page state.<p> 168 * 169 * @param cms the cms context 170 * @param currentPageUri the current page uri 171 * @param containerPage the container page bean with the current container state 172 * @param detailContentId the detail content structure id 173 * @param req the http request 174 * @param res the http response 175 * @param isDragMode if the page is in drag mode 176 * @param locale the content locale 177 * 178 * @throws CmsException if something goes wrong 179 */ 180 public CmsElementUtil( 181 CmsObject cms, 182 String currentPageUri, 183 CmsContainerPageBean containerPage, 184 CmsUUID detailContentId, 185 HttpServletRequest req, 186 HttpServletResponse res, 187 boolean isDragMode, 188 Locale locale) 189 throws CmsException { 190 191 m_cms = OpenCms.initCmsObject(cms); 192 m_req = req; 193 m_res = res; 194 m_currentPageUri = currentPageUri; 195 m_locale = locale; 196 // initializing request for standard context bean 197 req.setAttribute(CmsJspStandardContextBean.ATTRIBUTE_CMS_OBJECT, m_cms); 198 if (detailContentId != null) { 199 CmsResource detailRes = m_cms.readResource( 200 detailContentId, 201 CmsResourceFilter.ignoreExpirationOffline(m_cms)); 202 req.setAttribute(CmsDetailPageResourceHandler.ATTR_DETAIL_CONTENT_RESOURCE, detailRes); 203 } 204 m_standardContext = CmsJspStandardContextBean.getInstance(req); 205 m_page = m_cms.readResource(currentPageUri); 206 m_standardContext.setPage(containerPage); 207 m_standardContext.setDragMode(isDragMode); 208 } 209 210 /** 211 * Creates a new instance.<p> 212 * 213 * @param cms the cms context 214 * @param currentPageUri the current page uri 215 * @param detailContentId the detail content structure id 216 * @param req the http request 217 * @param res the http response 218 * @param locale the content locale 219 * 220 * @throws CmsException if something goes wrong 221 */ 222 public CmsElementUtil( 223 CmsObject cms, 224 String currentPageUri, 225 CmsUUID detailContentId, 226 HttpServletRequest req, 227 HttpServletResponse res, 228 Locale locale) 229 throws CmsException { 230 231 m_cms = OpenCms.initCmsObject(cms); 232 m_req = req; 233 m_res = res; 234 m_currentPageUri = currentPageUri; 235 m_locale = locale; 236 // initializing request for standard context bean 237 req.setAttribute(CmsJspStandardContextBean.ATTRIBUTE_CMS_OBJECT, m_cms); 238 if (detailContentId != null) { 239 CmsResource detailRes = m_cms.readResource(detailContentId, CmsResourceFilter.ignoreExpirationOffline(cms)); 240 req.setAttribute(CmsDetailPageResourceHandler.ATTR_DETAIL_CONTENT_RESOURCE, detailRes); 241 } 242 m_standardContext = CmsJspStandardContextBean.getInstance(req); 243 m_page = m_cms.readResource(currentPageUri); 244 CmsXmlContainerPage xmlContainerPage = CmsXmlContainerPageFactory.unmarshal(cms, m_page, req); 245 CmsContainerPageBean containerPage = xmlContainerPage.getContainerPage(cms); 246 m_standardContext.setPage(containerPage); 247 } 248 249 /** 250 * Creates a new instance.<p> 251 * 252 * @param cms the cms context 253 * @param currentPageUri the current page uri 254 * @param detailContentId the detail content structure id 255 * @param requestParameters the request parameters to use while rendering the elements 256 * @param req the http request 257 * @param res the http response 258 * @param locale the content locale 259 * 260 * @throws CmsException if something goes wrong 261 */ 262 public CmsElementUtil( 263 CmsObject cms, 264 String currentPageUri, 265 CmsUUID detailContentId, 266 String requestParameters, 267 HttpServletRequest req, 268 HttpServletResponse res, 269 Locale locale) 270 throws CmsException { 271 272 this(cms, currentPageUri, detailContentId, req, res, locale); 273 m_parameterMap = parseRequestParameters(requestParameters); 274 } 275 276 /** 277 * Checks if a group element is allowed in a container with a given type.<p> 278 * 279 * @param containerType the container type spec (comma separated) 280 * @param groupContainer the group 281 * 282 * @return true if the group is allowed in the container 283 */ 284 public static boolean checkGroupAllowed(String containerType, CmsGroupContainerBean groupContainer) { 285 286 return !Sets.intersection(CmsContainer.splitType(containerType), groupContainer.getTypes()).isEmpty(); 287 } 288 289 /** 290 * Helper method to create a string template source for a given formatter and content. 291 * 292 * @param formatter the formatter 293 * @param contentSupplier the content supplier 294 * 295 * @return the string template provider 296 */ 297 public static Function<String, String> createStringTemplateSource( 298 I_CmsFormatterBean formatter, 299 Supplier<CmsXmlContent> contentSupplier) { 300 301 return key -> { 302 String result = null; 303 if (formatter != null) { 304 result = formatter.getAttributes().get(key); 305 } 306 if (result == null) { 307 CmsXmlContent content = contentSupplier.get(); 308 if (content != null) { 309 result = content.getHandler().getParameter(key); 310 } 311 } 312 return result; 313 }; 314 } 315 316 /** 317 * Returns the formatter bean for the given element and container.<p> 318 * 319 * @param cms the cms context 320 * @param element the element to render 321 * @param container the container 322 * @param config the configuration data 323 * @param cache the session cache 324 * 325 * @return the formatter bean 326 */ 327 public static I_CmsFormatterBean getFormatterForContainer( 328 CmsObject cms, 329 CmsContainerElementBean element, 330 CmsContainer container, 331 CmsADEConfigData config, 332 CmsADESessionCache cache) { 333 334 I_CmsFormatterBean formatter = null; 335 CmsFormatterConfiguration formatterSet = config.getFormatters(cms, element.getResource()); 336 Map<String, I_CmsFormatterBean> formatters = formatterSet.getFormatterSelection( 337 container.getType(), 338 container.getWidth()); 339 String formatterId = element.getIndividualSettings().get( 340 CmsFormatterConfig.getSettingsKeyForContainer(container.getName())); 341 if (formatterId != null) { 342 if (CmsUUID.isValidUUID(formatterId)) { 343 formatter = OpenCms.getADEManager().getCachedFormatters(false).getFormatters().get( 344 new CmsUUID(formatterId)); 345 } else { 346 formatter = formatters.get(formatterId); 347 } 348 } 349 if (formatter == null) { 350 formatterId = element.getIndividualSettings().get(CmsFormatterConfig.FORMATTER_SETTINGS_KEY); 351 if (formatterId != null) { 352 formatter = formatters.get(formatterId); 353 } 354 } 355 if (formatter == null) { 356 // check for formatter config id stored for other containers matching the current container 357 for (Entry<String, String> settingsEntry : element.getIndividualSettings().entrySet()) { 358 if (settingsEntry.getKey().startsWith(CmsFormatterConfig.FORMATTER_SETTINGS_KEY)) { 359 formatter = formatters.get(settingsEntry.getValue()); 360 if (formatter != null) { 361 break; 362 } 363 } 364 } 365 } 366 if ((formatter == null) && (element.getFormatterId() != null)) { 367 for (I_CmsFormatterBean currentFormatter : formatters.values()) { 368 if ((currentFormatter.getJspStructureId() != null) 369 && currentFormatter.getJspStructureId().equals(element.getFormatterId())) { 370 formatter = currentFormatter; 371 break; 372 } 373 } 374 } 375 if (formatter == null) { 376 formatter = getStartFormatter(cms, container, config, element, cache); 377 } 378 return formatter; 379 } 380 381 /** 382 * Returns the start formatter for a newly dropped element.<p> 383 * This will be either the least recently used matching formatter or the default formatter.<p> 384 * 385 * @param cms the cms context 386 * @param cnt the container 387 * @param configData the configuration data 388 * @param element the container element 389 * @param cache the session cache 390 * 391 * @return the formatter bean 392 */ 393 private static I_CmsFormatterBean getStartFormatter( 394 CmsObject cms, 395 CmsContainer cnt, 396 CmsADEConfigData configData, 397 CmsContainerElementBean element, 398 CmsADESessionCache cache) { 399 400 I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(element.getResource()); 401 I_CmsFormatterBean formatter = cache.getRecentFormatter(type.getTypeName(), cnt, configData); 402 if (formatter == null) { 403 formatter = configData.getFormatters(cms, element.getResource()).getDefaultFormatter( 404 cnt.getType(), 405 cnt.getWidth()); 406 } 407 return formatter; 408 } 409 410 /** 411 * Returns the HTML content for the given resource and container.<p> 412 * 413 * @param elementFile the element resource file 414 * @param elementId the element id 415 * @param container the container 416 * 417 * @return the HTML content 418 */ 419 public String getContentByContainer(CmsFile elementFile, String elementId, CmsContainer container) { 420 421 CmsContainerElementBean element = CmsADESessionCache.getCache(m_req, m_cms).getCacheContainerElement(elementId); 422 element = element.clone(); 423 element.setTemporaryFile(elementFile); 424 CmsFormatterConfiguration configs = getFormatterConfiguration(element.getResource()); 425 return getContentByContainer(element, container, configs); 426 } 427 428 /** 429 * Returns the data for an element.<p> 430 * 431 * @param page the current container page 432 * @param element the resource 433 * @param containers the containers on the current container page 434 * 435 * @return the data for an element 436 * 437 * @throws CmsException if something goes wrong 438 */ 439 public CmsContainerElementData getElementData( 440 CmsResource page, 441 CmsContainerElementBean element, 442 Collection<CmsContainer> containers) 443 throws CmsException { 444 445 Locale requestLocale = m_cms.getRequestContext().getLocale(); 446 m_cms.getRequestContext().setLocale(m_locale); 447 element.initResource(m_cms); 448 if (element.getResource().isFolder()) { 449 return null; 450 } 451 CmsContainerElementData elementData = getBaseElementData(page, element); 452 CmsFormatterConfiguration formatterConfiguraton = getFormatterConfiguration(element.getResource()); 453 Map<String, String> contents = new HashMap<String, String>(); 454 if (element.isGroupContainer(m_cms)) { 455 Map<String, CmsContainer> containersByName = new HashMap<String, CmsContainer>(); 456 for (CmsContainer container : containers) { 457 containersByName.put(container.getName(), container); 458 } 459 CmsXmlGroupContainer xmlGroupContainer = CmsXmlGroupContainerFactory.unmarshal( 460 m_cms, 461 element.getResource(), 462 m_req); 463 CmsGroupContainerBean groupContainer = xmlGroupContainer.getGroupContainer(m_cms); 464 // make sure to use the content title and not the property title 465 elementData.setTitle(groupContainer.getTitle()); 466 elementData.setTypes(groupContainer.getTypes()); 467 elementData.setDescription(groupContainer.getDescription()); 468 if (groupContainer.getTypes().isEmpty()) { 469 if (groupContainer.getElements().isEmpty()) { 470 String emptySub = "<div>NEW AND EMPTY</div>"; 471 for (CmsContainer cont : containersByName.values()) { 472 if (formatterConfiguraton.hasFormatter(cont.getType(), cont.getWidth())) { 473 contents.put(cont.getName(), emptySub); 474 } 475 } 476 } else { 477 // TODO: throw appropriate exception 478 return null; 479 } 480 } else { 481 482 // add formatter and content entries for the supported types 483 for (CmsContainer cnt : containersByName.values()) { 484 485 String type = cnt.getType(); 486 if (checkGroupAllowed(type, groupContainer)) { 487 contents.put(cnt.getName(), "<div>should not be used</div>"); 488 } 489 } 490 } 491 // add subitems 492 List<String> subItems = new ArrayList<String>(); 493 494 for (CmsContainerElementBean subElement : groupContainer.getElements()) { 495 // collect ids 496 subItems.add(subElement.editorHash()); 497 } 498 elementData.setSubItems(subItems); 499 } else if (element.isInheritedContainer(m_cms)) { 500 CmsInheritanceReferenceParser parser = new CmsInheritanceReferenceParser(m_cms); 501 parser.parse(element.getResource()); 502 CmsInheritanceReference ref = parser.getReference(m_locale); 503 String name = null; 504 // check for new inheritance reference 505 if (ref != null) { 506 name = ref.getName(); 507 elementData.setDescription(ref.getDescription()); 508 elementData.setTitle(ref.getTitle()); 509 } 510 for (CmsContainer container : containers) { 511 if (formatterConfiguraton.hasFormatter(container.getType(), container.getWidth())) { 512 contents.put(container.getName(), "<div>should not be used</div>"); 513 } 514 } 515 516 List<CmsInheritanceInfo> inheritanceInfos = new ArrayList<CmsInheritanceInfo>(); 517 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(name)) { 518 CmsInheritedContainerState result = OpenCms.getADEManager().getInheritedContainerState( 519 m_cms, 520 m_cms.addSiteRoot(m_currentPageUri), 521 name); 522 for (CmsContainerElementBean subElement : result.getElements(true)) { 523 CmsInheritanceInfo inheritanceInfo = subElement.getInheritanceInfo(); 524 inheritanceInfo.setClientId(subElement.editorHash()); 525 inheritanceInfos.add(inheritanceInfo); 526 } 527 } else { 528 // setting a new id for name, will be persisted once the inheritance reference is edited and saved. 529 // use the structure id so it will always be the same for the resource 530 name = element.getResource().getStructureId().toString(); 531 } 532 elementData.setInheritanceInfos(inheritanceInfos); 533 elementData.setInheritanceName(name); 534 } else { 535 536 for (CmsContainer cnt : containers) { 537 boolean missesFormatterSetting = !elementData.getSettings().containsKey( 538 CmsFormatterConfig.getSettingsKeyForContainer(cnt.getName())); 539 if (missesFormatterSetting) { 540 if (element.getFormatterId() == null) { 541 I_CmsFormatterBean formatter = getStartFormatter( 542 m_cms, 543 cnt, 544 m_adeConfig, 545 element, 546 CmsADESessionCache.getCache(m_req, m_cms)); 547 if (formatter != null) { 548 elementData.getSettings().put( 549 CmsFormatterConfig.getSettingsKeyForContainer(cnt.getName()), 550 formatter.getId()); 551 element.addFormatterSetting(cnt.getName(), formatter.getId()); 552 } 553 } else { 554 Map<String, I_CmsFormatterBean> formatterSelection = formatterConfiguraton.getFormatterSelection( 555 cnt.getType(), 556 cnt.getWidth()); 557 for (Entry<String, I_CmsFormatterBean> formatterEntry : formatterSelection.entrySet()) { 558 I_CmsFormatterBean formatter = formatterEntry.getValue(); 559 String id = formatterEntry.getKey(); 560 if (element.getFormatterId().equals(formatter.getJspStructureId())) { 561 elementData.getSettings().put( 562 CmsFormatterConfig.getSettingsKeyForContainer(cnt.getName()), 563 id); 564 break; 565 } 566 } 567 } 568 } 569 } 570 // get the formatter configuration 571 Map<String, String> contentsByName = getContentsByContainerName(element, containers); 572 contents = contentsByName; 573 } 574 elementData.setContents(contents); 575 m_cms.getRequestContext().setLocale(requestLocale); 576 return elementData; 577 } 578 579 /** 580 * Returns the formatter and settings config data for an element.<p> 581 * 582 * @param page the current container page 583 * @param element the resource 584 * @param containerId the parent container id 585 * @param containers the containers on the current container page 586 * 587 * @return the data for an element 588 * 589 * @throws CmsException if something goes wrong 590 */ 591 public CmsElementSettingsConfig getElementSettingsConfig( 592 CmsResource page, 593 CmsContainerElementBean element, 594 String containerId, 595 Collection<CmsContainer> containers) 596 throws CmsException { 597 598 Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms); 599 Locale requestLocale = m_cms.getRequestContext().getLocale(); 600 m_cms.getRequestContext().setLocale(m_locale); 601 element.initResource(m_cms); 602 if (element.getResource().isFolder()) { 603 return null; 604 } 605 String schema = null; 606 try { 607 I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(element.getResource()); 608 schema = type.getConfiguration().getString("schema", null); 609 } catch (Exception e) { 610 LOG.error(e.getLocalizedMessage(), e); 611 } 612 613 CmsContainerElementData elementData = getBaseElementData(page, element); 614 Supplier<CmsXmlContent> contentSupplier = Suppliers.memoize(() -> { 615 try { 616 return CmsXmlContentFactory.unmarshal(m_cms, m_cms.readFile(element.getResource())); 617 } catch (CmsException e) { 618 LOG.error(e.getLocalizedMessage(), e); 619 return null; 620 } 621 }); 622 if (!element.isGroupContainer(m_cms) && !element.isInheritedContainer(m_cms)) { 623 CmsFormatterConfiguration formatterConfiguraton = getFormatterConfiguration(element.getResource()); 624 Map<String, Map<String, CmsFormatterConfig>> formatters = new HashMap<String, Map<String, CmsFormatterConfig>>(); 625 for (CmsContainer cnt : containers) { 626 if (cnt.getName().equals(containerId)) { 627 Map<String, CmsFormatterConfig> containerFormatters = new LinkedHashMap<String, CmsFormatterConfig>(); 628 boolean missesFormatterSetting = !elementData.getSettings().containsKey( 629 CmsFormatterConfig.getSettingsKeyForContainer(cnt.getName())); 630 Map<String, I_CmsFormatterBean> formatterSelection = formatterConfiguraton.getFormatterSelection( 631 cnt.getType(), 632 cnt.getWidth()); 633 for (Entry<String, I_CmsFormatterBean> formatterEntry : formatterSelection.entrySet()) { 634 I_CmsFormatterBean formatter = formatterEntry.getValue(); 635 String id = formatterEntry.getKey(); 636 if (missesFormatterSetting 637 && ((element.getFormatterId() == null) 638 || element.getFormatterId().equals(formatter.getJspStructureId()))) { 639 elementData.getSettings().put( 640 CmsFormatterConfig.getSettingsKeyForContainer(cnt.getName()), 641 id); 642 missesFormatterSetting = false; 643 } 644 String label = formatter.getNiceName(OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms)); 645 if (formatterEntry.getKey().startsWith(CmsFormatterConfig.SCHEMA_FORMATTER_ID)) { 646 label = Messages.get().getBundle().key(Messages.GUI_SCHEMA_FORMATTER_LABEL_0) 647 + " [" 648 + CmsResource.getName(formatter.getJspRootPath()) 649 + "]"; 650 } 651 if (CmsStringUtil.isEmptyOrWhitespaceOnly(label)) { 652 label = id; 653 } 654 CmsFormatterConfig config = new CmsFormatterConfig(id); 655 Set<String> cssResources = new LinkedHashSet<String>(); 656 for (String cssSitePath : formatter.getCssHeadIncludes()) { 657 cssResources.add(OpenCms.getLinkManager().getOnlineLink(m_cms, cssSitePath)); 658 } 659 config.setCssResources(cssResources); 660 config.setInlineCss(formatter.getInlineCss()); 661 config.setLabel(label); 662 config.setDescription( 663 formatter.getDescription(OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms))); 664 Map<String, CmsXmlContentProperty> settingsConfig = OpenCms.getADEManager().getFormatterSettings( 665 m_cms, 666 formatter, 667 element.getResource(), 668 m_locale, 669 m_req); 670 Function<String, String> templateSource = createStringTemplateSource( 671 formatter, 672 contentSupplier); 673 settingsConfig = CmsXmlContentPropertyHelper.resolveMacrosForPropertyInfo( 674 m_cms, 675 page, 676 element.getResource(), 677 contentSupplier, 678 templateSource, 679 settingsConfig); 680 config.setSettingConfig(settingsConfig); 681 List<I_CmsFormatterBean> nestedFormatters = OpenCms.getADEManager().getNestedFormatters( 682 m_cms, 683 element.getResource(), 684 m_locale, 685 m_req); 686 if ((nestedFormatters != null) && !nestedFormatters.isEmpty()) { 687 Map<String, String> settingPrefixes = new LinkedHashMap<String, String>(); 688 for (I_CmsFormatterBean nested : nestedFormatters) { 689 settingPrefixes.put( 690 nested.getId(), 691 nested.getNiceName(OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms))); 692 } 693 config.setNestedFormatterPrefixes(settingPrefixes); 694 } 695 696 config.setJspRootPath(formatter.getJspRootPath()); 697 containerFormatters.put(id, config); 698 } 699 formatters.put(cnt.getName(), containerFormatters); 700 } 701 } 702 elementData.setFormatters(formatters); 703 } 704 705 m_cms.getRequestContext().setLocale(requestLocale); 706 ArrayList<CmsAdditionalInfoBean> infos = new ArrayList<>(); 707 708 CmsResource resource = element.getResource(); 709 String resTypeName = OpenCms.getResourceManager().getResourceType(resource).getTypeName(); 710 CmsExplorerTypeSettings cmsExplorerTypeSettings = OpenCms.getWorkplaceManager().getExplorerTypeSetting( 711 resTypeName); 712 if (null == cmsExplorerTypeSettings) { 713 CmsMessageContainer errMsg = org.opencms.gwt.Messages.get().container( 714 org.opencms.gwt.Messages.ERR_EXPLORER_TYPE_SETTINGS_FOR_RESOURCE_TYPE_NOT_FOUND_3, 715 resource.getRootPath(), 716 resTypeName, 717 Integer.valueOf(resource.getTypeId())); 718 throw new CmsConfigurationException(errMsg); 719 } 720 String key = cmsExplorerTypeSettings.getKey(); 721 Locale currentLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms); 722 CmsMessages messages = OpenCms.getWorkplaceManager().getMessages(currentLocale); 723 String resTypeNiceName = messages.key(key); 724 infos.add( 725 new CmsAdditionalInfoBean( 726 messages.key(org.opencms.workplace.commons.Messages.GUI_LABEL_TYPE_0), 727 resTypeNiceName, 728 null)); 729 730 try { 731 CmsRelationFilter filter = CmsRelationFilter.relationsFromStructureId( 732 element.getResource().getStructureId()).filterType(CmsRelationType.XSD); 733 for (CmsRelation relation : m_cms.readRelations(filter)) { 734 CmsResource target = relation.getTarget(m_cms, CmsResourceFilter.IGNORE_EXPIRATION); 735 String label = Messages.get().getBundle(wpLocale).key(Messages.GUI_ADDINFO_SCHEMA_0); 736 infos.add(new CmsAdditionalInfoBean(label, target.getRootPath(), null)); 737 break; 738 } 739 } catch (CmsException e) { 740 LOG.error(e.getLocalizedMessage(), e); 741 } 742 743 CmsResourceState state = element.getResource().getState(); 744 return new CmsElementSettingsConfig(elementData, state, infos, schema); 745 } 746 747 /** 748 * Gets the container page.<p> 749 * 750 * @return the container page resource 751 */ 752 public CmsResource getPage() { 753 754 return m_page; 755 } 756 757 /** 758 * Sets the data to the given container element.<p> 759 * 760 * @param elementBean the element bean 761 * @param result the container element to set the data to 762 * 763 * @return the container element 764 * 765 * @throws CmsException if something goes wrong 766 */ 767 public CmsContainerElement setElementInfo(CmsContainerElementBean elementBean, CmsContainerElement result) 768 throws CmsException { 769 770 Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms); 771 // reinitializing resource to avoid caching issues 772 elementBean.initResource(m_cms); 773 CmsResource resource = elementBean.getResource(); 774 boolean isModelGroup = elementBean.getIndividualSettings().containsKey(CmsContainerElement.MODEL_GROUP_ID); 775 if (isModelGroup) { 776 CmsUUID groupId = new CmsUUID(elementBean.getIndividualSettings().get(CmsContainerElement.MODEL_GROUP_ID)); 777 resource = m_cms.readResource(groupId, CmsResourceFilter.IGNORE_EXPIRATION); 778 } 779 780 boolean newEditorDisabled = !CmsWorkplaceEditorManager.checkAcaciaEditorAvailable( 781 m_cms, 782 elementBean.getResource()); 783 result.setNewEditorDisabled(newEditorDisabled); 784 I_CmsResourceType resourceType = OpenCms.getResourceManager().getResourceType(resource); 785 String typeName = resourceType.getTypeName(); 786 result.setHasEditHandler( 787 (resourceType instanceof CmsResourceTypeXmlContent) 788 && (((CmsResourceTypeXmlContent)resourceType).getEditHandler(m_cms) != null)); 789 result.setResourceType(typeName); 790 result.setIconClasses( 791 CmsIconUtil.getIconClasses( 792 CmsIconUtil.getDisplayType(m_cms, resource), 793 elementBean.getResource().getName(), 794 false)); 795 CmsPermissionInfo permissionInfo; 796 String title; 797 String subTitle; 798 if (!elementBean.isInMemoryOnly()) { 799 permissionInfo = OpenCms.getADEManager().getPermissionInfo(m_cms, resource, m_page.getRootPath()); 800 if (CmsResourceTypeXmlContent.isXmlContent(resource)) { 801 if (CmsStringUtil.isEmptyOrWhitespaceOnly(permissionInfo.getNoEditReason()) 802 && elementBean.isInheritedContainer(m_cms)) { 803 String requestUri = m_cms.getRequestContext().getUri(); 804 String folderPath = CmsResource.getFolderPath(requestUri); 805 String configPath = CmsStringUtil.joinPaths( 806 folderPath, 807 CmsContainerConfigurationCache.INHERITANCE_CONFIG_FILE_NAME); 808 if (m_cms.existsResource(configPath)) { 809 permissionInfo.setNoEditReason( 810 new CmsResourceUtil(m_cms, m_cms.readResource(configPath)).getNoEditReason(wpLocale, true)); 811 } else { 812 if (!m_cms.getLock(folderPath).isLockableBy(m_cms.getRequestContext().getCurrentUser())) { 813 permissionInfo.setNoEditReason( 814 org.opencms.workplace.explorer.Messages.get().getBundle(wpLocale).key( 815 org.opencms.workplace.explorer.Messages.GUI_NO_EDIT_REASON_LOCK_1, 816 new CmsResourceUtil(m_cms, m_cms.readResource(folderPath)).getLockedByName())); 817 } 818 } 819 } 820 } else { 821 permissionInfo.setNoEditReason( 822 Messages.get().getBundle(wpLocale).key(Messages.GUI_ELEMENT_RESOURCE_CAN_NOT_BE_EDITED_0)); 823 } 824 CmsGallerySearchResult searchResult = CmsGallerySearch.searchById( 825 m_cms, 826 resource.getStructureId(), 827 m_locale); 828 title = searchResult.getTitle(); 829 if (CmsStringUtil.isEmptyOrWhitespaceOnly(title)) { 830 resource.getName(); 831 } 832 subTitle = searchResult.getUserLastModified(); 833 Date lastModDate = searchResult.getDateLastModified(); 834 if (lastModDate != null) { 835 subTitle += " / " + CmsDateUtil.getDateTime(lastModDate, DateFormat.MEDIUM, wpLocale); 836 } 837 } else { 838 permissionInfo = new CmsPermissionInfo(true, true, ""); 839 title = CmsWorkplaceMessages.getResourceTypeName(wpLocale, typeName); 840 subTitle = CmsWorkplaceMessages.getResourceTypeDescription(wpLocale, typeName); 841 } 842 result.setTitle(title); 843 result.setSubTitle(subTitle); 844 result.setClientId(elementBean.editorHash()); 845 result.setSitePath(m_cms.getSitePath(resource)); 846 847 result.setCreateNew(elementBean.isCreateNew()); 848 CmsResourceTypeConfig typeConfig = getConfigData().getResourceType(typeName); 849 if (typeConfig != null) { 850 result.setCopyInModels(typeConfig.isCopyInModels()); 851 } 852 Map<CmsUUID, CmsElementView> viewMap = OpenCms.getADEManager().getElementViews(m_cms); 853 854 boolean isModelGroupEditing = CmsModelGroupHelper.isModelGroupResource(m_page); 855 if (!isModelGroup 856 && isModelGroupEditing 857 && elementBean.getIndividualSettings().containsKey(CmsContainerElement.MODEL_GROUP_STATE) 858 && (ModelGroupState.isModelGroup == ModelGroupState.evaluate( 859 elementBean.getIndividualSettings().get(CmsContainerElement.MODEL_GROUP_STATE)))) { 860 isModelGroup = true; 861 } 862 if (!isModelGroupEditing && isModelGroup) { 863 CmsResourceTypeConfig modelGroupConfig = getConfigData().getResourceType( 864 CmsResourceTypeXmlContainerPage.MODEL_GROUP_TYPE_NAME); 865 if (modelGroupConfig != null) { 866 CmsUUID elementView = modelGroupConfig.getElementView(); 867 CmsElementView viewObject = viewMap.get(elementView); 868 if ((viewObject != null) && (viewObject.getParentViewId() != null)) { 869 elementView = viewObject.getParentViewId(); 870 } 871 result.setElementView(elementView); 872 } 873 } else if (typeConfig != null) { 874 CmsUUID elementView = typeConfig.getElementView(); 875 CmsElementView viewObject = viewMap.get(elementView); 876 if ((viewObject != null) && (viewObject.getParentViewId() != null)) { 877 elementView = viewObject.getParentViewId(); 878 } 879 result.setElementView(elementView); 880 } 881 if (elementBean.isCreateNew() 882 && CmsStringUtil.isEmptyOrWhitespaceOnly(permissionInfo.getNoEditReason()) 883 && ((typeConfig == null) 884 || !typeConfig.checkCreatable(m_cms, CmsResource.getParentFolder(m_page.getRootPath())))) { 885 String niceName = CmsWorkplaceMessages.getResourceTypeName(wpLocale, typeName); 886 permissionInfo.setNoEditReason( 887 Messages.get().getBundle(wpLocale).key(Messages.GUI_CONTAINERPAGE_TYPE_NOT_CREATABLE_1, niceName)); 888 } 889 result.setHasSettings(hasSettings(m_cms, elementBean.getResource())); 890 result.setPermissionInfo(permissionInfo); 891 result.setReleasedAndNotExpired(elementBean.isReleasedAndNotExpired()); 892 if (elementBean.isModelGroup()) { 893 String modelId = elementBean.getIndividualSettings().get(CmsContainerElement.MODEL_GROUP_ID); 894 result.setModelGroupId(modelId != null ? new CmsUUID(modelId) : CmsUUID.getNullUUID()); 895 } 896 result.setWasModelGroup( 897 elementBean.getIndividualSettings().containsKey(CmsContainerElement.MODEL_GROUP_STATE) 898 && (ModelGroupState.evaluate( 899 elementBean.getIndividualSettings().get( 900 CmsContainerElement.MODEL_GROUP_STATE)) == ModelGroupState.wasModelGroup)); 901 return result; 902 } 903 904 /** 905 * Returns the formatter configuration for the given element resource.<p> 906 * 907 * @param resource the element resource 908 * 909 * @return the formatter configuration 910 */ 911 CmsFormatterConfiguration getFormatterConfiguration(CmsResource resource) { 912 913 return getConfigData().getFormatters(m_cms, resource); 914 } 915 916 /** 917 * Returns the base element data for the given element bean, without content or formatter info.<p> 918 * 919 * @param page the current container page 920 * @param element the resource 921 * 922 * @return base element data 923 * 924 * @throws CmsException in case reading the data fails 925 */ 926 private CmsContainerElementData getBaseElementData(CmsResource page, CmsContainerElementBean element) 927 throws CmsException { 928 929 CmsResourceUtil resUtil = new CmsResourceUtil(m_cms, element.getResource()); 930 CmsContainerElementData elementData = new CmsContainerElementData(); 931 setElementInfo(element, elementData); 932 elementData.setLoadTime(System.currentTimeMillis()); 933 elementData.setLastModifiedDate(element.getResource().getDateLastModified()); 934 elementData.setLastModifiedByUser(m_cms.readUser(element.getResource().getUserLastModified()).getName()); 935 elementData.setNavText(resUtil.getNavText()); 936 Map<String, CmsXmlContentProperty> settingConfig = CmsXmlContentPropertyHelper.getPropertyInfo( 937 m_cms, 938 page, 939 element.getResource()); 940 elementData.setSettings( 941 CmsXmlContentPropertyHelper.convertPropertiesToClientFormat( 942 m_cms, 943 element.getIndividualSettings(), 944 settingConfig)); 945 return elementData; 946 } 947 948 /** 949 * Returns the ADE configuration data for the current URI.<p> 950 * 951 * @return the ADE configuration data 952 */ 953 private CmsADEConfigData getConfigData() { 954 955 if (m_adeConfig == null) { 956 m_adeConfig = OpenCms.getADEManager().lookupConfiguration(m_cms, m_cms.addSiteRoot(m_currentPageUri)); 957 } 958 return m_adeConfig; 959 } 960 961 /** 962 * Returns the HTML content of the given element and container.<p> 963 * 964 * @param element the element 965 * @param container the container 966 * @param configs the formatter configurations 967 * 968 * @return the HTML content 969 */ 970 private String getContentByContainer( 971 CmsContainerElementBean element, 972 CmsContainer container, 973 CmsFormatterConfiguration configs) { 974 975 String content = null; 976 977 I_CmsFormatterBean formatter = getFormatterForContainer( 978 m_cms, 979 element, 980 container, 981 m_adeConfig, 982 CmsADESessionCache.getCache(m_req, m_cms)); 983 if (formatter != null) { 984 element = element.clone(); // clone element because presets for different containers may be different 985 element.initSettings(m_cms, formatter, m_locale, m_req, container.getSettingPresets()); 986 try { 987 content = getElementContent(element, m_cms.readResource(formatter.getJspStructureId()), container); 988 // } 989 } catch (Exception e) { 990 LOG.error(e.getLocalizedMessage(), e); 991 } 992 if (content != null) { 993 content = removeScriptTags(content); 994 } 995 } 996 return content; 997 } 998 999 /** 1000 * Returns the rendered element content for all the given containers. 1001 * 1002 * @param element the element to render 1003 * @param containers the containers the element appears in 1004 * 1005 * @return a map from container names to rendered page contents 1006 */ 1007 private Map<String, String> getContentsByContainerName( 1008 CmsContainerElementBean element, 1009 Collection<CmsContainer> containers) { 1010 1011 CmsFormatterConfiguration configs = getFormatterConfiguration(element.getResource()); 1012 Map<String, String> result = new HashMap<String, String>(); 1013 for (CmsContainer container : containers) { 1014 String content = getContentByContainer(element, container, configs); 1015 if (content != null) { 1016 content = removeScriptTags(content); 1017 } 1018 result.put(container.getName(), content); 1019 } 1020 return result; 1021 } 1022 1023 /** 1024 * Returns the content of an element when rendered with the given formatter.<p> 1025 * 1026 * @param element the element bean 1027 * @param formatter the formatter uri 1028 * @param container the container for which the element content should be retrieved 1029 * 1030 * @return generated html code 1031 * 1032 * @throws CmsException if an cms related error occurs 1033 * @throws ServletException if a jsp related error occurs 1034 * 1035 * @throws IOException if a jsp related error occurs 1036 */ 1037 private String getElementContent(CmsContainerElementBean element, CmsResource formatter, CmsContainer container) 1038 throws CmsException, ServletException, IOException { 1039 1040 element.initResource(m_cms); 1041 TemplateBean templateBean = CmsADESessionCache.getCache(m_req, m_cms).getTemplateBean( 1042 m_cms.addSiteRoot(m_currentPageUri), 1043 true); 1044 String oldUri = m_cms.getRequestContext().getUri(); 1045 try { 1046 m_cms.getRequestContext().setUri(m_currentPageUri); 1047 CmsContainerBean containerBean = null; 1048 if ((m_standardContext.getPage() != null) 1049 && m_standardContext.getPage().getContainers().containsKey(container.getName())) { 1050 containerBean = m_standardContext.getPage().getContainers().get(container.getName()); 1051 } else { 1052 containerBean = new CmsContainerBean( 1053 container.getName(), 1054 container.getType(), 1055 container.getParentInstanceId(), 1056 container.isRootContainer(), 1057 container.getMaxElements(), 1058 Collections.<CmsContainerElementBean> emptyList()); 1059 } 1060 if (containerBean.getWidth() == null) { 1061 containerBean.setWidth(String.valueOf(container.getWidth())); 1062 } 1063 containerBean.setDetailOnly(container.isDetailOnly()); 1064 m_standardContext.setContainer(containerBean); 1065 m_standardContext.setElement(element); 1066 m_standardContext.setEdited(true); 1067 // to enable 'old' direct edit features for content-collector-elements, 1068 // set the direct-edit-provider-attribute in the request 1069 I_CmsDirectEditProvider eb = new CmsAdvancedDirectEditProvider(); 1070 eb.init(m_cms, CmsDirectEditMode.TRUE, element.getSitePath()); 1071 m_req.setAttribute(I_CmsDirectEditProvider.ATTRIBUTE_DIRECT_EDIT_PROVIDER, eb); 1072 m_req.setAttribute(CmsTemplateContextManager.ATTR_TEMPLATE_BEAN, templateBean); 1073 String encoding = m_res.getCharacterEncoding(); 1074 return (new String( 1075 OpenCms.getResourceManager().getLoader(formatter).dump(m_cms, formatter, null, m_locale, m_req, m_res), 1076 encoding)).trim(); 1077 } finally { 1078 m_cms.getRequestContext().setUri(oldUri); 1079 } 1080 } 1081 1082 /** 1083 * Helper method for checking whether there are properties defined for a given content element.<p> 1084 * 1085 * @param cms the CmsObject to use for VFS operations 1086 * @param resource the resource for which it should be checked whether it has properties 1087 * 1088 * @return true if the resource has properties defined 1089 * 1090 * @throws CmsException if something goes wrong 1091 */ 1092 private boolean hasSettings(CmsObject cms, CmsResource resource) throws CmsException { 1093 1094 if (!CmsResourceTypeXmlContent.isXmlContent(resource)) { 1095 return false; 1096 } 1097 1098 CmsFormatterConfiguration formatters = getConfigData().getFormatters(m_cms, resource); 1099 boolean result = (formatters.getAllFormatters().size() > 1) 1100 || !CmsXmlContentPropertyHelper.getPropertyInfo(m_cms, null, resource).isEmpty(); 1101 if (!result && (formatters.getAllFormatters().size() == 1)) { 1102 result = (formatters.getAllFormatters().get(0).getSettings() != null) 1103 && (formatters.getAllFormatters().get(0).getSettings().size() > 0); 1104 } 1105 return result; 1106 } 1107 1108 /** 1109 * Parses the given request parameters string into a parameter map.<p> 1110 * 1111 * @param requestParameters the request parameters to parse 1112 * 1113 * @return the parameter map 1114 */ 1115 private Map<String, Object> parseRequestParameters(String requestParameters) { 1116 1117 Map<String, Object> parameterMap; 1118 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(requestParameters)) { 1119 parameterMap = new HashMap<String, Object>(); 1120 String[] params = requestParameters.split("&"); 1121 for (int i = 0; i < params.length; i++) { 1122 int position = params[i].indexOf("="); 1123 if (position >= 0) { 1124 String key = params[i].substring(0, position); 1125 String value = params[i].substring(position + 1); 1126 if (value.contains(",")) { 1127 parameterMap.put(key, value.split(",")); 1128 } else { 1129 parameterMap.put(key, value); 1130 } 1131 } 1132 } 1133 } else { 1134 parameterMap = Collections.<String, Object> emptyMap(); 1135 } 1136 return parameterMap; 1137 } 1138 1139 /** 1140 * Removes all script tags from given input.<p> 1141 * 1142 * @param input the input to remove script tags from 1143 * 1144 * @return the cleaned input 1145 */ 1146 private String removeScriptTags(String input) { 1147 1148 Pattern removePattern = Pattern.compile("<script[^>]*?>[\\s\\S]*?<\\/script>"); 1149 Matcher match = removePattern.matcher(input); 1150 return match.replaceAll(""); 1151 } 1152}