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.CmsResourceTypeConfig; 032import org.opencms.ade.containerpage.shared.CmsContainerElement; 033import org.opencms.ade.containerpage.shared.CmsContainerElement.ModelGroupState; 034import org.opencms.ade.containerpage.shared.CmsFormatterConfig; 035import org.opencms.file.CmsObject; 036import org.opencms.file.CmsProperty; 037import org.opencms.file.CmsPropertyDefinition; 038import org.opencms.file.CmsResource; 039import org.opencms.file.CmsResourceFilter; 040import org.opencms.file.CmsUser; 041import org.opencms.file.types.CmsResourceTypeXmlContainerPage; 042import org.opencms.file.types.I_CmsResourceType; 043import org.opencms.flex.CmsFlexController; 044import org.opencms.lock.CmsLock; 045import org.opencms.main.CmsException; 046import org.opencms.main.CmsLog; 047import org.opencms.main.OpenCms; 048import org.opencms.util.CmsStringUtil; 049import org.opencms.util.CmsUUID; 050import org.opencms.xml.containerpage.CmsADESessionCache; 051import org.opencms.xml.containerpage.CmsContainerBean; 052import org.opencms.xml.containerpage.CmsContainerElementBean; 053import org.opencms.xml.containerpage.CmsContainerPageBean; 054import org.opencms.xml.containerpage.CmsXmlContainerPage; 055import org.opencms.xml.containerpage.CmsXmlContainerPageFactory; 056import org.opencms.xml.containerpage.I_CmsFormatterBean; 057 058import java.io.IOException; 059import java.util.ArrayList; 060import java.util.Collections; 061import java.util.HashMap; 062import java.util.HashSet; 063import java.util.List; 064import java.util.Locale; 065import java.util.Map; 066import java.util.Map.Entry; 067import java.util.Set; 068 069import javax.servlet.http.HttpServletRequest; 070import javax.servlet.http.HttpServletResponse; 071 072import org.apache.commons.logging.Log; 073 074/** 075 * Handles all model group specific tasks.<p> 076 */ 077public class CmsModelGroupHelper { 078 079 /** The name of the container storing the groups base element. */ 080 public static final String MODEL_GROUP_BASE_CONTAINER = "base_container"; 081 082 /** Static reference to the log. */ 083 private static final Log LOG = CmsLog.getLog(CmsModelGroupHelper.class); 084 085 /** Settings to keep when resetting. */ 086 private static final String[] KEEP_SETTING_IDS = new String[] { 087 CmsContainerElement.MODEL_GROUP_STATE, 088 CmsContainerElement.ELEMENT_INSTANCE_ID, 089 CmsContainerElement.USE_AS_COPY_MODEL}; 090 091 /** The current cms context. */ 092 private CmsObject m_cms; 093 094 /** The session cache instance. */ 095 private CmsADESessionCache m_sessionCache; 096 097 /** The configuration data of the current container page location. */ 098 private CmsADEConfigData m_configData; 099 100 /** Indicating the edit model groups mode. */ 101 private boolean m_isEditingModelGroups; 102 103 /** 104 * Constructor.<p> 105 * 106 * @param cms the current cms context 107 * @param configData the configuration data 108 * @param sessionCache the session cache 109 * @param isEditingModelGroups the edit model groups flag 110 */ 111 public CmsModelGroupHelper( 112 CmsObject cms, 113 CmsADEConfigData configData, 114 CmsADESessionCache sessionCache, 115 boolean isEditingModelGroups) { 116 117 m_cms = cms; 118 m_sessionCache = sessionCache; 119 m_configData = configData; 120 m_isEditingModelGroups = isEditingModelGroups; 121 } 122 123 /** 124 * Creates a new model group resource.<p> 125 * 126 * @param cms the current cms context 127 * @param configData the configuration data 128 * 129 * @return the new resource 130 * 131 * @throws CmsException in case creating the resource fails 132 */ 133 public static CmsResource createModelGroup(CmsObject cms, CmsADEConfigData configData) throws CmsException { 134 135 CmsResourceTypeConfig typeConfig = configData.getResourceType( 136 CmsResourceTypeXmlContainerPage.MODEL_GROUP_TYPE_NAME); 137 return typeConfig.createNewElement(cms, configData.getBasePath()); 138 } 139 140 /** 141 * Returns if the given resource is a model group resource.<p> 142 * 143 * @param resource the resource 144 * 145 * @return <code>true</code> if the given resource is a model group resource 146 */ 147 public static boolean isModelGroupResource(CmsResource resource) { 148 149 return CmsResourceTypeXmlContainerPage.MODEL_GROUP_TYPE_NAME.equals( 150 OpenCms.getResourceManager().getResourceType(resource).getTypeName()); 151 } 152 153 /** 154 * Updates a model group resource to the changed data structure.<p> 155 * This step is necessary when updating from version 10.0.x to 10.5.x.<p> 156 * 157 * @param cms the cms context 158 * @param group the model group resource 159 * @param baseContainerName the new base container name 160 * 161 * @return <code>true</code> if the resource was updated 162 */ 163 public static boolean updateModelGroupResource(CmsObject cms, CmsResource group, String baseContainerName) { 164 165 if (!isModelGroupResource(group)) { 166 // skip resources that are no model group 167 return false; 168 } 169 try { 170 CmsXmlContainerPage xmlContainerPage = CmsXmlContainerPageFactory.unmarshal(cms, group); 171 CmsContainerPageBean pageBean = xmlContainerPage.getContainerPage(cms); 172 173 CmsContainerBean baseContainer = pageBean.getContainers().get(MODEL_GROUP_BASE_CONTAINER); 174 boolean changedContent = false; 175 if ((baseContainer != null) && CmsStringUtil.isNotEmptyOrWhitespaceOnly(baseContainerName)) { 176 177 List<CmsContainerBean> containers = new ArrayList<CmsContainerBean>(); 178 for (CmsContainerBean container : pageBean.getContainers().values()) { 179 if (container.getName().equals(MODEL_GROUP_BASE_CONTAINER)) { 180 CmsContainerBean replacer = new CmsContainerBean( 181 baseContainerName, 182 container.getType(), 183 container.getParentInstanceId(), 184 container.isRootContainer(), 185 container.getElements()); 186 containers.add(replacer); 187 changedContent = true; 188 } else { 189 containers.add(container); 190 } 191 } 192 if (changedContent) { 193 pageBean = new CmsContainerPageBean(containers); 194 } 195 } 196 if (changedContent) { 197 ensureLock(cms, group); 198 199 if (changedContent) { 200 xmlContainerPage.save(cms, pageBean); 201 } 202 if (group.getName().endsWith(".xml")) { 203 // renaming model groups so they will be rendered correctly by the browser 204 String targetPath = cms.getSitePath(group); 205 targetPath = targetPath.substring(0, targetPath.length() - 4) + ".html"; 206 cms.renameResource(cms.getSitePath(group), targetPath); 207 group = cms.readResource(group.getStructureId()); 208 } 209 tryUnlock(cms, group); 210 return true; 211 } 212 return false; 213 214 } catch (CmsException e) { 215 LOG.error(e.getLocalizedMessage(), e); 216 return false; 217 } 218 219 } 220 221 /** 222 * Updates model group resources to the changed data structure.<p> 223 * This step is necessary when updating from version 10.0.x to 10.5.x.<p> 224 * 225 * @param request the request 226 * @param response the response 227 * @param basePath the path to the model group, or the base path to search for model groups 228 * @param baseContainerName the new base container name 229 * 230 * @throws IOException in case writing to the response fails 231 */ 232 public static void updateModelGroupResources( 233 HttpServletRequest request, 234 HttpServletResponse response, 235 String basePath, 236 String baseContainerName) 237 throws IOException { 238 239 if (CmsFlexController.isCmsRequest(request)) { 240 241 try { 242 CmsFlexController controller = CmsFlexController.getController(request); 243 CmsObject cms = controller.getCmsObject(); 244 CmsResource base = cms.readResource(basePath); 245 List<CmsResource> resources; 246 I_CmsResourceType groupType = OpenCms.getResourceManager().getResourceType( 247 CmsResourceTypeXmlContainerPage.MODEL_GROUP_TYPE_NAME); 248 if (base.isFolder()) { 249 resources = cms.readResources( 250 basePath, 251 CmsResourceFilter.ONLY_VISIBLE_NO_DELETED.addRequireType(groupType)); 252 } else if (OpenCms.getResourceManager().getResourceType(base).equals(groupType)) { 253 resources = Collections.singletonList(base); 254 } else { 255 resources = Collections.emptyList(); 256 } 257 258 if (resources.isEmpty()) { 259 response.getWriter().println("No model group resources found at " + basePath + "<br />"); 260 } else { 261 for (CmsResource group : resources) { 262 boolean updated = updateModelGroupResource(cms, group, baseContainerName); 263 response.getWriter().println( 264 "Group '" + group.getRootPath() + "' was updated " + updated + "<br />"); 265 } 266 } 267 } catch (CmsException e) { 268 LOG.error(e.getLocalizedMessage(), e); 269 e.printStackTrace(response.getWriter()); 270 } 271 } 272 } 273 274 /** 275 * Locks the given resource.<p> 276 * 277 * @param cms the cms context 278 * @param resource the resource to lock 279 * 280 * @throws CmsException in case locking fails 281 */ 282 private static void ensureLock(CmsObject cms, CmsResource resource) throws CmsException { 283 284 CmsUser user = cms.getRequestContext().getCurrentUser(); 285 CmsLock lock = cms.getLock(resource); 286 if (!lock.isOwnedBy(user)) { 287 cms.lockResourceTemporary(resource); 288 } else if (!lock.isOwnedInProjectBy(user, cms.getRequestContext().getCurrentProject())) { 289 cms.changeLock(resource); 290 } 291 } 292 293 /** 294 * Tries to unlock a resource.<p> 295 * 296 * @param cms the cms context 297 * @param resource the resource to unlock 298 */ 299 private static void tryUnlock(CmsObject cms, CmsResource resource) { 300 301 try { 302 cms.unlockResource(resource); 303 } catch (CmsException e) { 304 LOG.debug("Unable to unlock " + resource.getRootPath(), e); 305 } 306 } 307 308 /** 309 * Adds the model group elements to the page.<p> 310 * 311 * @param elements the requested elements 312 * @param foundGroups list to add the found group element client ids to 313 * @param page the page 314 * @param alwaysCopy <code>true</code> to create element copies in case of non model groups and createNew is set 315 * @param locale the content locale 316 * 317 * @return the adjusted page 318 * 319 * @throws CmsException in case something goes wrong 320 */ 321 public CmsContainerPageBean prepareforModelGroupContent( 322 Map<String, CmsContainerElementBean> elements, 323 List<String> foundGroups, 324 CmsContainerPageBean page, 325 boolean alwaysCopy, 326 Locale locale) 327 throws CmsException { 328 329 for (Entry<String, CmsContainerElementBean> entry : elements.entrySet()) { 330 CmsContainerElementBean element = entry.getValue(); 331 CmsContainerPageBean modelPage = null; 332 String modelInstanceId = null; 333 boolean foundInstance = false; 334 if (CmsModelGroupHelper.isModelGroupResource(element.getResource())) { 335 modelPage = getContainerPageBean(element.getResource()); 336 CmsContainerElementBean baseElement = getModelBaseElement(modelPage, element.getResource()); 337 if (baseElement == null) { 338 break; 339 } 340 String baseInstanceId = baseElement.getInstanceId(); 341 String originalInstanceId = element.getInstanceId(); 342 element = getModelReplacementElement(element, baseElement, true); 343 List<CmsContainerBean> modelContainers = readModelContainers( 344 baseInstanceId, 345 originalInstanceId, 346 modelPage, 347 baseElement.isCopyModel()); 348 if (!m_isEditingModelGroups && baseElement.isCopyModel()) { 349 modelContainers = createNewElementsForModelGroup(m_cms, modelContainers, locale); 350 } 351 modelContainers.addAll(page.getContainers().values()); 352 page = new CmsContainerPageBean(modelContainers); 353 // update the entry element value, as the settings will have changed 354 entry.setValue(element); 355 if (m_sessionCache != null) { 356 // also update the session cache 357 m_sessionCache.setCacheContainerElement(element.editorHash(), element); 358 } 359 } else { 360 // here we need to make sure to remove the source container page setting and to set a new element instance id 361 362 Map<String, String> settings = new HashMap<String, String>(element.getIndividualSettings()); 363 String source = settings.get(CmsContainerpageService.SOURCE_CONTAINERPAGE_ID_SETTING); 364 settings.remove(CmsContainerpageService.SOURCE_CONTAINERPAGE_ID_SETTING); 365 // TODO: Make sure source id is available for second call 366 367 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(source)) { 368 try { 369 CmsUUID sourceId = new CmsUUID(source); 370 CmsResource sourcePage = m_cms.readResource(sourceId); 371 if (CmsResourceTypeXmlContainerPage.isContainerPage(sourcePage)) { 372 CmsXmlContainerPage xmlCnt = CmsXmlContainerPageFactory.unmarshal( 373 m_cms, 374 m_cms.readFile(sourcePage)); 375 modelPage = xmlCnt.getContainerPage(m_cms); 376 modelInstanceId = element.getInstanceId(); 377 } 378 379 settings.remove(CmsContainerElement.ELEMENT_INSTANCE_ID); 380 381 boolean copyRoot = false; 382 if (alwaysCopy && (modelInstanceId != null) && (modelPage != null)) { 383 for (CmsContainerElementBean el : modelPage.getElements()) { 384 if (modelInstanceId.equals(el.getInstanceId())) { 385 copyRoot = el.isCreateNew(); 386 break; 387 } 388 } 389 } 390 391 if (copyRoot) { 392 CmsObject cloneCms = OpenCms.initCmsObject(m_cms); 393 cloneCms.getRequestContext().setLocale(locale); 394 String typeName = OpenCms.getResourceManager().getResourceType( 395 element.getResource()).getTypeName(); 396 CmsResourceTypeConfig typeConfig = m_configData.getResourceType(typeName); 397 if (typeConfig == null) { 398 throw new IllegalArgumentException( 399 "Can not copy template model element '" 400 + element.getResource().getRootPath() 401 + "' because the resource type '" 402 + typeName 403 + "' is not available in this sitemap."); 404 } 405 CmsResource newResource = typeConfig.createNewElement( 406 cloneCms, 407 element.getResource(), 408 m_configData.getBasePath()); 409 410 element = new CmsContainerElementBean( 411 newResource.getStructureId(), 412 element.getFormatterId(), 413 settings, 414 false); 415 } else { 416 element = CmsContainerElementBean.cloneWithSettings(element, settings); 417 } 418 if (modelPage != null) { 419 Map<String, List<CmsContainerBean>> containerByParent = new HashMap<String, List<CmsContainerBean>>(); 420 421 for (CmsContainerBean container : modelPage.getContainers().values()) { 422 if (container.getParentInstanceId() != null) { 423 if (!containerByParent.containsKey(container.getParentInstanceId())) { 424 containerByParent.put( 425 container.getParentInstanceId(), 426 new ArrayList<CmsContainerBean>()); 427 } 428 containerByParent.get(container.getParentInstanceId()).add(container); 429 } 430 if (!foundInstance) { 431 for (CmsContainerElementBean child : container.getElements()) { 432 if (modelInstanceId == null) { 433 if (child.getId().equals(element.getId())) { 434 modelInstanceId = child.getInstanceId(); 435 foundInstance = true; 436 // we also want to keep the settings of the model group 437 Map<String, String> setting = new HashMap<String, String>( 438 child.getIndividualSettings()); 439 setting.remove(CmsContainerElement.ELEMENT_INSTANCE_ID); 440 element = CmsContainerElementBean.cloneWithSettings(element, setting); 441 break; 442 } 443 } else { 444 if (modelInstanceId.equals(child.getInstanceId())) { 445 foundInstance = true; 446 break; 447 } 448 } 449 } 450 } 451 } 452 if (foundInstance && containerByParent.containsKey(modelInstanceId)) { 453 List<CmsContainerBean> modelContainers = collectModelStructure( 454 modelInstanceId, 455 element.getInstanceId(), 456 containerByParent, 457 true); 458 if (alwaysCopy) { 459 modelContainers = createNewElementsForModelGroup(m_cms, modelContainers, locale); 460 } 461 foundGroups.add(element.editorHash()); 462 modelContainers.addAll(page.getContainers().values()); 463 page = new CmsContainerPageBean(modelContainers); 464 } 465 } 466 467 // update the entry element value, as the settings will have changed 468 entry.setValue(element); 469 if (m_sessionCache != null) { 470 // also update the session cache 471 m_sessionCache.setCacheContainerElement(element.editorHash(), element); 472 } 473 } catch (Exception e) { 474 LOG.info(e.getLocalizedMessage(), e); 475 } 476 477 } 478 } 479 480 } 481 return page; 482 } 483 484 /** 485 * Reads the present model groups and merges their containers into the page.<p> 486 * 487 * @param page the container page 488 * 489 * @return the resulting container page 490 */ 491 public CmsContainerPageBean readModelGroups(CmsContainerPageBean page) { 492 493 List<CmsContainerBean> resultContainers = new ArrayList<CmsContainerBean>(); 494 for (CmsContainerBean container : page.getContainers().values()) { 495 boolean hasModels = false; 496 List<CmsContainerElementBean> elements = new ArrayList<CmsContainerElementBean>(); 497 for (CmsContainerElementBean element : container.getElements()) { 498 try { 499 element.initResource(m_cms); 500 if (isModelGroupResource(element.getResource())) { 501 hasModels = true; 502 CmsContainerPageBean modelGroupPage = getContainerPageBean(element.getResource()); 503 CmsContainerElementBean baseElement = getModelBaseElement( 504 modelGroupPage, 505 element.getResource()); 506 if (baseElement == null) { 507 LOG.error( 508 "Error rendering model group '" 509 + element.getResource().getRootPath() 510 + "', base element could not be determind."); 511 continue; 512 } 513 String baseInstanceId = baseElement.getInstanceId(); 514 CmsContainerElementBean replaceElement = getModelReplacementElement( 515 element, 516 baseElement, 517 false); 518 if (m_sessionCache != null) { 519 m_sessionCache.setCacheContainerElement(replaceElement.editorHash(), replaceElement); 520 } 521 elements.add(replaceElement); 522 resultContainers.addAll( 523 readModelContainers( 524 baseInstanceId, 525 element.getInstanceId(), 526 modelGroupPage, 527 baseElement.isCopyModel())); 528 } else { 529 elements.add(element); 530 } 531 } catch (CmsException e) { 532 LOG.info(e.getLocalizedMessage(), e); 533 } 534 } 535 if (hasModels) { 536 resultContainers.add( 537 new CmsContainerBean( 538 container.getName(), 539 container.getType(), 540 container.getParentInstanceId(), 541 container.isRootContainer(), 542 container.getMaxElements(), 543 elements)); 544 } else { 545 resultContainers.add(container); 546 } 547 } 548 return new CmsContainerPageBean(resultContainers); 549 } 550 551 /** 552 * Removes the model group containers.<p> 553 * 554 * @param page the container page state 555 * 556 * @return the container page without the model group containers 557 */ 558 public CmsContainerPageBean removeModelGroupContainers(CmsContainerPageBean page) { 559 560 Map<String, List<CmsContainerBean>> containersByParent = getContainerByParent(page); 561 Set<String> modelInstances = new HashSet<String>(); 562 for (CmsContainerElementBean element : page.getElements()) { 563 if (element.getIndividualSettings().containsKey(CmsContainerElement.MODEL_GROUP_ID)) { 564 modelInstances.add(element.getInstanceId()); 565 } 566 } 567 568 Set<String> descendingInstances = new HashSet<String>(); 569 for (String modelInstance : modelInstances) { 570 descendingInstances.addAll(collectDescendingInstances(modelInstance, containersByParent)); 571 } 572 List<CmsContainerBean> containers = new ArrayList<CmsContainerBean>(); 573 for (CmsContainerBean container : page.getContainers().values()) { 574 if ((container.getParentInstanceId() == null) 575 || !descendingInstances.contains(container.getParentInstanceId())) { 576 // iterate the container elements to replace the model group elements 577 List<CmsContainerElementBean> elements = new ArrayList<CmsContainerElementBean>(); 578 for (CmsContainerElementBean element : container.getElements()) { 579 if (modelInstances.contains(element.getInstanceId())) { 580 CmsUUID modelId = new CmsUUID( 581 element.getIndividualSettings().get(CmsContainerElement.MODEL_GROUP_ID)); 582 CmsContainerElementBean replacer = new CmsContainerElementBean( 583 modelId, 584 element.getFormatterId(), 585 element.getIndividualSettings(), 586 false); 587 elements.add(replacer); 588 } else { 589 elements.add(element); 590 } 591 } 592 containers.add( 593 new CmsContainerBean( 594 container.getName(), 595 container.getType(), 596 container.getParentInstanceId(), 597 container.isRootContainer(), 598 container.getMaxElements(), 599 elements)); 600 601 } 602 } 603 return new CmsContainerPageBean(containers); 604 } 605 606 /** 607 * Saves the model groups of the given container page.<p> 608 * 609 * @param page the container page 610 * @param pageResource the model group resource 611 * 612 * @return the container page referencing the saved model groups 613 * 614 * @throws CmsException in case writing the page properties fails 615 */ 616 public CmsContainerPageBean saveModelGroups(CmsContainerPageBean page, CmsResource pageResource) 617 throws CmsException { 618 619 CmsUUID modelElementId = null; 620 CmsContainerElementBean baseElement = null; 621 for (CmsContainerElementBean element : page.getElements()) { 622 if (element.isModelGroup()) { 623 modelElementId = element.getId(); 624 baseElement = element; 625 break; 626 627 } 628 } 629 List<CmsContainerBean> containers = new ArrayList<CmsContainerBean>(); 630 for (CmsContainerBean container : page.getContainers().values()) { 631 List<CmsContainerElementBean> elements = new ArrayList<CmsContainerElementBean>(); 632 boolean hasChanges = false; 633 for (CmsContainerElementBean element : container.getElements()) { 634 if (element.isModelGroup() && !element.getId().equals(modelElementId)) { 635 // there should not be another model group element, remove the model group settings 636 Map<String, String> settings = new HashMap<String, String>(element.getIndividualSettings()); 637 settings.remove(CmsContainerElement.MODEL_GROUP_ID); 638 settings.remove(CmsContainerElement.MODEL_GROUP_STATE); 639 elements.add( 640 new CmsContainerElementBean(element.getId(), element.getFormatterId(), settings, false)); 641 hasChanges = true; 642 } else { 643 elements.add(element); 644 } 645 } 646 if (hasChanges) { 647 containers.add( 648 new CmsContainerBean( 649 container.getName(), 650 container.getType(), 651 container.getParentInstanceId(), 652 container.isRootContainer(), 653 container.getMaxElements(), 654 elements)); 655 } else { 656 containers.add(container); 657 } 658 659 } 660 661 List<CmsProperty> changedProps = new ArrayList<CmsProperty>(); 662 if (baseElement != null) { 663 String val = Boolean.parseBoolean( 664 baseElement.getIndividualSettings().get(CmsContainerElement.USE_AS_COPY_MODEL)) 665 ? CmsContainerElement.USE_AS_COPY_MODEL 666 : ""; 667 changedProps.add(new CmsProperty(CmsPropertyDefinition.PROPERTY_TEMPLATE_ELEMENTS, val, val)); 668 } 669 m_cms.writePropertyObjects(pageResource, changedProps); 670 671 return new CmsContainerPageBean(containers); 672 673 } 674 675 /** 676 * Adjusts formatter settings and initializes a new instance id for the given container element.<p> 677 * 678 * @param element the container element 679 * @param originalContainer the original parent container name 680 * @param adjustedContainer the target container name 681 * @param setNewInstanceId <code>true</code> to set a new instance id 682 * 683 * @return the new element instance 684 */ 685 private CmsContainerElementBean adjustSettings( 686 CmsContainerElementBean element, 687 String originalContainer, 688 String adjustedContainer, 689 boolean setNewInstanceId) { 690 691 Map<String, String> settings = new HashMap<String, String>(element.getIndividualSettings()); 692 if (setNewInstanceId) { 693 settings.put(CmsContainerElement.ELEMENT_INSTANCE_ID, new CmsUUID().toString()); 694 } 695 String formatterId = settings.remove(CmsFormatterConfig.getSettingsKeyForContainer(originalContainer)); 696 settings.put(CmsFormatterConfig.getSettingsKeyForContainer(adjustedContainer), formatterId); 697 return CmsContainerElementBean.cloneWithSettings(element, settings); 698 } 699 700 /** 701 * Returns the descending instance id's to the given element instance.<p> 702 * 703 * @param instanceId the instance id 704 * @param containersByParent the container page containers by parent instance id 705 * 706 * @return the containers 707 */ 708 private Set<String> collectDescendingInstances( 709 String instanceId, 710 Map<String, List<CmsContainerBean>> containersByParent) { 711 712 Set<String> descendingInstances = new HashSet<String>(); 713 descendingInstances.add(instanceId); 714 if (containersByParent.containsKey(instanceId)) { 715 for (CmsContainerBean container : containersByParent.get(instanceId)) { 716 for (CmsContainerElementBean element : container.getElements()) { 717 descendingInstances.addAll(collectDescendingInstances(element.getInstanceId(), containersByParent)); 718 } 719 } 720 } 721 return descendingInstances; 722 } 723 724 /** 725 * Collects the model group structure.<p> 726 * 727 * @param modelInstanceId the model instance id 728 * @param replaceModelId the local instance id 729 * @param containerByParent the model group page containers by parent instance id 730 * @param isCopyGroup <code>true</code> in case of a copy group 731 * 732 * @return the collected containers 733 */ 734 private List<CmsContainerBean> collectModelStructure( 735 String modelInstanceId, 736 String replaceModelId, 737 Map<String, List<CmsContainerBean>> containerByParent, 738 boolean isCopyGroup) { 739 740 List<CmsContainerBean> result = new ArrayList<CmsContainerBean>(); 741 742 if (containerByParent.containsKey(modelInstanceId)) { 743 for (CmsContainerBean container : containerByParent.get(modelInstanceId)) { 744 String adjustedContainerName = replaceModelId + container.getName().substring(modelInstanceId.length()); 745 746 List<CmsContainerElementBean> elements = new ArrayList<CmsContainerElementBean>(); 747 for (CmsContainerElementBean element : container.getElements()) { 748 CmsContainerElementBean copyElement = adjustSettings( 749 element, 750 container.getName(), 751 adjustedContainerName, 752 isCopyGroup); 753 if (m_sessionCache != null) { 754 m_sessionCache.setCacheContainerElement(copyElement.editorHash(), copyElement); 755 } 756 elements.add(copyElement); 757 result.addAll( 758 collectModelStructure( 759 element.getInstanceId(), 760 copyElement.getInstanceId(), 761 containerByParent, 762 isCopyGroup)); 763 } 764 765 result.add( 766 new CmsContainerBean( 767 adjustedContainerName, 768 container.getType(), 769 replaceModelId, 770 container.isRootContainer(), 771 container.getMaxElements(), 772 elements)); 773 } 774 } 775 return result; 776 } 777 778 /** 779 * Creates new resources for elements marked with create as new.<p> 780 * 781 * @param cms the cms context 782 * @param modelContainers the model containers 783 * @param locale the content locale 784 * 785 * @return the updated model containers 786 * 787 * @throws CmsException in case something goes wrong 788 */ 789 private List<CmsContainerBean> createNewElementsForModelGroup( 790 CmsObject cms, 791 List<CmsContainerBean> modelContainers, 792 Locale locale) 793 throws CmsException { 794 795 Map<CmsUUID, CmsResource> newResources = new HashMap<CmsUUID, CmsResource>(); 796 CmsObject cloneCms = OpenCms.initCmsObject(cms); 797 cloneCms.getRequestContext().setLocale(locale); 798 for (CmsContainerBean container : modelContainers) { 799 for (CmsContainerElementBean element : container.getElements()) { 800 if (element.isCreateNew() && !newResources.containsKey(element.getId())) { 801 element.initResource(cms); 802 String typeName = OpenCms.getResourceManager().getResourceType(element.getResource()).getTypeName(); 803 CmsResourceTypeConfig typeConfig = m_configData.getResourceType(typeName); 804 if (typeConfig == null) { 805 throw new IllegalArgumentException( 806 "Can not copy template model element '" 807 + element.getResource().getRootPath() 808 + "' because the resource type '" 809 + typeName 810 + "' is not available in this sitemap."); 811 } 812 CmsResource newResource = typeConfig.createNewElement( 813 cloneCms, 814 element.getResource(), 815 m_configData.getBasePath()); 816 newResources.put(element.getId(), newResource); 817 } 818 } 819 } 820 if (!newResources.isEmpty()) { 821 List<CmsContainerBean> updatedContainers = new ArrayList<CmsContainerBean>(); 822 for (CmsContainerBean container : modelContainers) { 823 List<CmsContainerElementBean> updatedElements = new ArrayList<CmsContainerElementBean>(); 824 for (CmsContainerElementBean element : container.getElements()) { 825 if (newResources.containsKey(element.getId())) { 826 CmsContainerElementBean newBean = new CmsContainerElementBean( 827 newResources.get(element.getId()).getStructureId(), 828 element.getFormatterId(), 829 element.getIndividualSettings(), 830 false); 831 updatedElements.add(newBean); 832 } else { 833 updatedElements.add(element); 834 } 835 } 836 CmsContainerBean updatedContainer = new CmsContainerBean( 837 container.getName(), 838 container.getType(), 839 container.getParentInstanceId(), 840 container.isRootContainer(), 841 container.getMaxElements(), 842 updatedElements); 843 updatedContainers.add(updatedContainer); 844 } 845 modelContainers = updatedContainers; 846 } 847 return modelContainers; 848 } 849 850 /** 851 * Collects the page containers by parent instance id.<p> 852 * 853 * @param page the page 854 * 855 * @return the containers by parent id 856 */ 857 private Map<String, List<CmsContainerBean>> getContainerByParent(CmsContainerPageBean page) { 858 859 Map<String, List<CmsContainerBean>> containerByParent = new HashMap<String, List<CmsContainerBean>>(); 860 861 for (CmsContainerBean container : page.getContainers().values()) { 862 if (container.getParentInstanceId() != null) { 863 if (!containerByParent.containsKey(container.getParentInstanceId())) { 864 containerByParent.put(container.getParentInstanceId(), new ArrayList<CmsContainerBean>()); 865 } 866 containerByParent.get(container.getParentInstanceId()).add(container); 867 } 868 } 869 return containerByParent; 870 } 871 872 /** 873 * Unmarshals the given resource.<p> 874 * 875 * @param resource the resource 876 * 877 * @return the container page bean 878 * 879 * @throws CmsException in case unmarshalling fails 880 */ 881 private CmsContainerPageBean getContainerPageBean(CmsResource resource) throws CmsException { 882 883 CmsXmlContainerPage xmlCnt = CmsXmlContainerPageFactory.unmarshal(m_cms, m_cms.readFile(resource)); 884 return xmlCnt.getContainerPage(m_cms); 885 } 886 887 /** 888 * Returns the model group base element.<p> 889 * 890 * @param modelGroupPage the model group page 891 * @param modelGroupResource the model group resource 892 * 893 * @return the base element 894 */ 895 private CmsContainerElementBean getModelBaseElement( 896 CmsContainerPageBean modelGroupPage, 897 CmsResource modelGroupResource) { 898 899 CmsContainerElementBean result = null; 900 for (CmsContainerElementBean element : modelGroupPage.getElements()) { 901 if (CmsContainerElement.ModelGroupState.isModelGroup.name().equals( 902 element.getIndividualSettings().get(CmsContainerElement.MODEL_GROUP_STATE))) { 903 result = element; 904 break; 905 } 906 } 907 return result; 908 } 909 910 /** 911 * Returns the the element to be rendered as the model group base.<p> 912 * 913 * @param element the original element 914 * @param baseElement the model group base 915 * @param allowCopyModel if copy models are allowed 916 * 917 * @return the element 918 */ 919 private CmsContainerElementBean getModelReplacementElement( 920 CmsContainerElementBean element, 921 CmsContainerElementBean baseElement, 922 boolean allowCopyModel) { 923 924 boolean resetSettings = false; 925 if (!baseElement.isCopyModel() && !baseElement.getFormatterId().equals(element.getFormatterId())) { 926 I_CmsFormatterBean formatter = m_configData.getCachedFormatters().getFormatters().get( 927 element.getFormatterId()); 928 resetSettings = (formatter == null) 929 || !formatter.getResourceTypeNames().contains( 930 OpenCms.getResourceManager().getResourceType(baseElement.getResource()).getTypeName()); 931 } 932 Map<String, String> settings; 933 if (resetSettings) { 934 settings = new HashMap<String, String>(); 935 for (String id : KEEP_SETTING_IDS) { 936 if (element.getIndividualSettings().containsKey(id)) { 937 settings.put(id, element.getIndividualSettings().get(id)); 938 } 939 } 940 settings.put(CmsContainerElement.MODEL_GROUP_ID, element.getId().toString()); 941 // transfer all other settings 942 for (Entry<String, String> settingEntry : baseElement.getIndividualSettings().entrySet()) { 943 if (!settings.containsKey(settingEntry.getKey())) { 944 settings.put(settingEntry.getKey(), settingEntry.getValue()); 945 } 946 } 947 } else { 948 settings = new HashMap<String, String>(element.getIndividualSettings()); 949 if (!(baseElement.isCopyModel() && allowCopyModel)) { 950 // skip the model id in case of copy models 951 settings.put(CmsContainerElement.MODEL_GROUP_ID, element.getId().toString()); 952 if (allowCopyModel) { 953 // transfer all other settings 954 for (Entry<String, String> settingEntry : baseElement.getIndividualSettings().entrySet()) { 955 if (!settings.containsKey(settingEntry.getKey())) { 956 settings.put(settingEntry.getKey(), settingEntry.getValue()); 957 } 958 } 959 } 960 961 } else if (baseElement.isCopyModel()) { 962 settings.put(CmsContainerElement.MODEL_GROUP_STATE, ModelGroupState.wasModelGroup.name()); 963 } 964 } 965 return CmsContainerElementBean.cloneWithSettings(baseElement, settings); 966 } 967 968 /** 969 * Returns the model containers.<p> 970 * 971 * @param modelInstanceId the model instance id 972 * @param localInstanceId the local instance id 973 * @param modelPage the model page bean 974 * @param isCopyGroup <code>true</code> in case of a copy group 975 * 976 * @return the model group containers 977 */ 978 private List<CmsContainerBean> readModelContainers( 979 String modelInstanceId, 980 String localInstanceId, 981 CmsContainerPageBean modelPage, 982 boolean isCopyGroup) { 983 984 Map<String, List<CmsContainerBean>> containerByParent = getContainerByParent(modelPage); 985 List<CmsContainerBean> modelContainers; 986 if (containerByParent.containsKey(modelInstanceId)) { 987 modelContainers = collectModelStructure(modelInstanceId, localInstanceId, containerByParent, isCopyGroup); 988 } else { 989 modelContainers = new ArrayList<CmsContainerBean>(); 990 } 991 return modelContainers; 992 } 993}