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.xml.containerpage; 029 030import org.opencms.ade.containerpage.CmsModelGroupHelper; 031import org.opencms.ade.containerpage.shared.CmsFormatterConfig; 032import org.opencms.file.CmsFile; 033import org.opencms.file.CmsObject; 034import org.opencms.file.CmsResource; 035import org.opencms.file.CmsResourceFilter; 036import org.opencms.i18n.CmsEncoder; 037import org.opencms.i18n.CmsLocaleManager; 038import org.opencms.main.CmsException; 039import org.opencms.main.CmsLog; 040import org.opencms.main.OpenCms; 041import org.opencms.relations.CmsLink; 042import org.opencms.relations.CmsRelationType; 043import org.opencms.util.CmsMacroResolver; 044import org.opencms.util.CmsUUID; 045import org.opencms.xml.CmsXmlContentDefinition; 046import org.opencms.xml.CmsXmlException; 047import org.opencms.xml.CmsXmlGenericWrapper; 048import org.opencms.xml.CmsXmlUtils; 049import org.opencms.xml.content.CmsXmlContent; 050import org.opencms.xml.content.CmsXmlContentMacroVisitor; 051import org.opencms.xml.content.CmsXmlContentProperty; 052import org.opencms.xml.content.CmsXmlContentPropertyHelper; 053import org.opencms.xml.page.CmsXmlPage; 054import org.opencms.xml.types.CmsXmlNestedContentDefinition; 055import org.opencms.xml.types.CmsXmlVfsFileValue; 056import org.opencms.xml.types.I_CmsXmlContentValue; 057import org.opencms.xml.types.I_CmsXmlSchemaType; 058 059import java.util.ArrayList; 060import java.util.Arrays; 061import java.util.Collections; 062import java.util.HashMap; 063import java.util.HashSet; 064import java.util.Iterator; 065import java.util.LinkedHashMap; 066import java.util.List; 067import java.util.Locale; 068import java.util.Map; 069import java.util.Set; 070 071import org.apache.commons.logging.Log; 072 073import org.dom4j.Document; 074import org.dom4j.Element; 075import org.xml.sax.EntityResolver; 076 077/** 078 * Implementation of a object used to access and manage the xml data of a container page.<p> 079 * 080 * In addition to the XML content interface. It also provides access to more comfortable beans. 081 * 082 * @since 7.5.2 083 * 084 * @see #getContainerPage(CmsObject) 085 */ 086public class CmsXmlContainerPage extends CmsXmlContent { 087 088 /** XML node name constants. */ 089 public enum XmlNode { 090 091 /** Container attribute node name. */ 092 Attribute, 093 /** Main node name. */ 094 Containers, 095 /** The create new element node name. */ 096 CreateNew, 097 /** Container elements node name. */ 098 Elements, 099 /** Element formatter node name. */ 100 Formatter, 101 /** The is root container node name. */ 102 IsRootContainer, 103 /** Container attribute key node name. */ 104 Key, 105 /** Container name node name. */ 106 Name, 107 /** Parent element instance id node name. */ 108 ParentInstanceId, 109 /** Container type node name. */ 110 Type, 111 /** Element URI node name. */ 112 Uri, 113 /** Container attribute value node name. */ 114 Value; 115 } 116 117 /** The log object for this class. */ 118 private static final Log LOG = CmsLog.getLog(CmsXmlContainerPage.class); 119 120 /** The container page objects. */ 121 private Map<Locale, CmsContainerPageBean> m_cntPages; 122 123 /** 124 * Hides the public constructor.<p> 125 */ 126 protected CmsXmlContainerPage() { 127 128 // noop 129 } 130 131 /** 132 * Creates a new container page based on the provided XML document.<p> 133 * 134 * The given encoding is used when marshalling the XML again later.<p> 135 * 136 * @param cms the cms context, if <code>null</code> no link validation is performed 137 * @param document the document to create the container page from 138 * @param encoding the encoding of the container page 139 * @param resolver the XML entity resolver to use 140 */ 141 protected CmsXmlContainerPage(CmsObject cms, Document document, String encoding, EntityResolver resolver) { 142 143 // must set document first to be able to get the content definition 144 m_document = document; 145 // for the next line to work the document must already be available 146 m_contentDefinition = getContentDefinition(resolver); 147 // initialize the XML content structure 148 initDocument(cms, m_document, encoding, m_contentDefinition); 149 } 150 151 /** 152 * Create a new container page based on the given default content, 153 * that will have all language nodes of the default content and ensures the presence of the given locale.<p> 154 * 155 * The given encoding is used when marshalling the XML again later.<p> 156 * 157 * @param cms the current users OpenCms content 158 * @param locale the locale to generate the default content for 159 * @param modelUri the absolute path to the container page file acting as model 160 * 161 * @throws CmsException in case the model file is not found or not valid 162 */ 163 protected CmsXmlContainerPage(CmsObject cms, Locale locale, String modelUri) 164 throws CmsException { 165 166 // init model from given modelUri 167 CmsFile modelFile = cms.readFile(modelUri, CmsResourceFilter.ONLY_VISIBLE_NO_DELETED); 168 CmsXmlContainerPage model = CmsXmlContainerPageFactory.unmarshal(cms, modelFile); 169 170 // initialize macro resolver to use on model file values 171 CmsMacroResolver macroResolver = CmsMacroResolver.newInstance().setCmsObject(cms); 172 173 // content definition must be set here since it's used during document creation 174 m_contentDefinition = model.getContentDefinition(); 175 // get the document from the default content 176 Document document = (Document)model.m_document.clone(); 177 // initialize the XML content structure 178 initDocument(cms, document, model.getEncoding(), m_contentDefinition); 179 // resolve eventual macros in the nodes 180 visitAllValuesWith(new CmsXmlContentMacroVisitor(cms, macroResolver)); 181 if (!hasLocale(locale)) { 182 // required locale not present, add it 183 try { 184 addLocale(cms, locale); 185 } catch (CmsXmlException e) { 186 // this can not happen since the locale does not exist 187 LOG.error(e.getMessage(), e); 188 } 189 } 190 } 191 192 /** 193 * Create a new container page based on the given content definition, 194 * that will have one language node for the given locale all initialized with default values.<p> 195 * 196 * The given encoding is used when marshalling the XML again later.<p> 197 * 198 * @param cms the current users OpenCms content 199 * @param locale the locale to generate the default content for 200 * @param encoding the encoding to use when marshalling the container page later 201 * @param contentDefinition the content definition to create the content for 202 */ 203 protected CmsXmlContainerPage( 204 CmsObject cms, 205 Locale locale, 206 String encoding, 207 CmsXmlContentDefinition contentDefinition) { 208 209 // content definition must be set here since it's used during document creation 210 m_contentDefinition = contentDefinition; 211 // create the XML document according to the content definition 212 Document document = m_contentDefinition.createDocument(cms, this, CmsLocaleManager.MASTER_LOCALE); 213 // initialize the XML content structure 214 initDocument(cms, document, encoding, m_contentDefinition); 215 } 216 217 /** 218 * Saves a container page bean to the in-memory XML structure and returns the changed content.<p> 219 * 220 * @param cms the current CMS context 221 * @param cntPage the container page bean 222 * @return the new content for the container page 223 * @throws CmsException if something goes wrong 224 */ 225 public byte[] createContainerPageXml(CmsObject cms, CmsContainerPageBean cntPage) throws CmsException { 226 227 // make sure all links are validated 228 writeContainerPage(cms, cntPage); 229 checkLinkConcistency(cms); 230 return marshal(); 231 232 } 233 234 /** 235 * Gets the container page content as a bean.<p> 236 * 237 * @param cms the current CMS context 238 * @return the bean containing the container page data 239 */ 240 public CmsContainerPageBean getContainerPage(CmsObject cms) { 241 242 Locale masterLocale = CmsLocaleManager.MASTER_LOCALE; 243 Locale localeToLoad = null; 244 // always use master locale if possible, otherwise use the first locale. 245 // this is important for 'legacy' container pages which were created before container pages became locale independent 246 if (m_cntPages.containsKey(masterLocale)) { 247 localeToLoad = masterLocale; 248 } else if (!m_cntPages.isEmpty()) { 249 localeToLoad = m_cntPages.keySet().iterator().next(); 250 } 251 if (localeToLoad == null) { 252 return null; 253 } else { 254 CmsContainerPageBean result = m_cntPages.get(localeToLoad); 255 return result; 256 } 257 } 258 259 /** 260 * @see org.opencms.xml.content.CmsXmlContent#isAutoCorrectionEnabled() 261 */ 262 @Override 263 public boolean isAutoCorrectionEnabled() { 264 265 return true; 266 } 267 268 /** 269 * Saves given container page in the current locale, and not only in memory but also to VFS.<p> 270 * 271 * @param cms the current cms context 272 * @param cntPage the container page to save 273 * 274 * @throws CmsException if something goes wrong 275 */ 276 public void save(CmsObject cms, CmsContainerPageBean cntPage) throws CmsException { 277 278 save(cms, cntPage, false); 279 } 280 281 /** 282 * Saves given container page in the current locale, and not only in memory but also to VFS.<p> 283 * 284 * @param cms the current cms context 285 * @param cntPage the container page to save 286 * @param ifChangedOnly <code>true</code> to only write the file if the content has changed 287 * 288 * @throws CmsException if something goes wrong 289 */ 290 public void save(CmsObject cms, CmsContainerPageBean cntPage, boolean ifChangedOnly) throws CmsException { 291 292 CmsFile file = getFile(); 293 byte[] data = createContainerPageXml(cms, cntPage); 294 if (ifChangedOnly && Arrays.equals(file.getContents(), data)) { 295 return; 296 } 297 // lock the file 298 cms.lockResourceTemporary(file); 299 file.setContents(data); 300 cms.writeFile(file); 301 } 302 303 /** 304 * Saves a container page in in-memory XML structure.<p> 305 * 306 * @param cms the current CMS context 307 * @param cntPage the container page bean to save 308 * 309 * @throws CmsException if something goes wrong 310 */ 311 public void writeContainerPage(CmsObject cms, CmsContainerPageBean cntPage) throws CmsException { 312 313 // keep unused containers 314 CmsContainerPageBean savePage = cleanupContainersContainers(cms, cntPage); 315 savePage = removeEmptyContainers(cntPage); 316 // Replace existing locales with master locale 317 for (Locale locale : getLocales()) { 318 removeLocale(locale); 319 } 320 Locale masterLocale = CmsLocaleManager.MASTER_LOCALE; 321 addLocale(cms, masterLocale); 322 323 // add the nodes to the raw XML structure 324 Element parent = getLocaleNode(masterLocale); 325 saveContainerPage(cms, parent, savePage); 326 initDocument(m_document, m_encoding, m_contentDefinition); 327 } 328 329 /** 330 * Checks the link consistency for a given locale and reinitializes the document afterwards.<p> 331 * 332 * @param cms the cms context 333 */ 334 protected void checkLinkConcistency(CmsObject cms) { 335 336 Locale masterLocale = CmsLocaleManager.MASTER_LOCALE; 337 338 for (I_CmsXmlContentValue contentValue : getValues(masterLocale)) { 339 if (contentValue instanceof CmsXmlVfsFileValue) { 340 CmsLink link = ((CmsXmlVfsFileValue)contentValue).getLink(cms); 341 link.checkConsistency(cms); 342 } 343 } 344 initDocument(); 345 } 346 347 /** 348 * Removes all empty containers and merges the containers of the current document that are not used in the given container page with it.<p> 349 * 350 * @param cms the current CMS context 351 * @param cntPage the container page to merge 352 * 353 * @return a new container page with the additional unused containers 354 */ 355 protected CmsContainerPageBean cleanupContainersContainers(CmsObject cms, CmsContainerPageBean cntPage) { 356 357 // get the used containers first 358 Map<String, CmsContainerBean> currentContainers = cntPage.getContainers(); 359 List<CmsContainerBean> containers = new ArrayList<CmsContainerBean>(); 360 for (String cntName : cntPage.getNames()) { 361 CmsContainerBean container = currentContainers.get(cntName); 362 if (!container.getElements().isEmpty()) { 363 containers.add(container); 364 } 365 } 366 367 // now get the unused containers 368 CmsContainerPageBean currentContainerPage = getContainerPage(cms); 369 if (currentContainerPage != null) { 370 for (String cntName : currentContainerPage.getNames()) { 371 if (!currentContainers.containsKey(cntName)) { 372 CmsContainerBean container = currentContainerPage.getContainers().get(cntName); 373 if (!container.getElements().isEmpty()) { 374 containers.add(container); 375 } 376 } 377 } 378 } 379 380 // check if any nested containers have lost their parent element 381 382 // first collect all present elements 383 Map<String, CmsContainerElementBean> pageElements = new HashMap<String, CmsContainerElementBean>(); 384 Map<String, String> parentContainers = new HashMap<String, String>(); 385 for (CmsContainerBean container : containers) { 386 for (CmsContainerElementBean element : container.getElements()) { 387 try { 388 element.initResource(cms); 389 390 if (!CmsModelGroupHelper.isModelGroupResource(element.getResource())) { 391 pageElements.put(element.getInstanceId(), element); 392 parentContainers.put(element.getInstanceId(), container.getName()); 393 } 394 } catch (CmsException e) { 395 LOG.warn(e.getLocalizedMessage(), e); 396 } 397 } 398 } 399 Iterator<CmsContainerBean> cntIt = containers.iterator(); 400 while (cntIt.hasNext()) { 401 CmsContainerBean container = cntIt.next(); 402 // check all unused nested containers if their parent element is still part of the page 403 if (!currentContainers.containsKey(container.getName()) 404 && (container.isNestedContainer() && !container.isRootContainer())) { 405 boolean remove = !pageElements.containsKey(container.getParentInstanceId()) 406 || container.getElements().isEmpty(); 407 if (!remove) { 408 // check if the parent element formatter is set to strictly render all nested containers 409 CmsContainerElementBean element = pageElements.get(container.getParentInstanceId()); 410 String settingsKey = CmsFormatterConfig.getSettingsKeyForContainer( 411 parentContainers.get(element.getInstanceId())); 412 String formatterId = element.getIndividualSettings().get(settingsKey); 413 if (CmsUUID.isValidUUID(formatterId)) { 414 I_CmsFormatterBean formatterBean = OpenCms.getADEManager().getCachedFormatters( 415 false).getFormatters().get(new CmsUUID(formatterId)); 416 remove = (formatterBean instanceof CmsFormatterBean) 417 && ((CmsFormatterBean)formatterBean).isStrictContainers(); 418 } 419 } 420 if (remove) { 421 // remove the sub elements from the page list 422 for (CmsContainerElementBean element : container.getElements()) { 423 pageElements.remove(element.getInstanceId()); 424 } 425 // remove the container 426 cntIt.remove(); 427 } 428 } 429 } 430 431 return new CmsContainerPageBean(containers); 432 } 433 434 /** 435 * Fills a {@link CmsXmlVfsFileValue} with the resource identified by the given id.<p> 436 * 437 * @param cms the current CMS context 438 * @param element the XML element to fill 439 * @param resourceId the ID identifying the resource to use 440 * 441 * @return the resource 442 * 443 * @throws CmsException if the resource can not be read 444 */ 445 protected CmsResource fillResource(CmsObject cms, Element element, CmsUUID resourceId) throws CmsException { 446 447 String xpath = element.getPath(); 448 int pos = xpath.lastIndexOf("/" + XmlNode.Containers.name() + "/"); 449 if (pos > 0) { 450 xpath = xpath.substring(pos + 1); 451 } 452 CmsRelationType type = getHandler().getRelationType(xpath); 453 CmsResource res = cms.readResource(resourceId, CmsResourceFilter.IGNORE_EXPIRATION); 454 CmsXmlVfsFileValue.fillEntry(element, res.getStructureId(), res.getRootPath(), type); 455 return res; 456 } 457 458 /** 459 * @see org.opencms.xml.content.CmsXmlContent#initDocument(org.opencms.file.CmsObject, org.dom4j.Document, java.lang.String, org.opencms.xml.CmsXmlContentDefinition) 460 */ 461 @Override 462 protected void initDocument(CmsObject cms, Document document, String encoding, CmsXmlContentDefinition definition) { 463 464 m_document = document; 465 m_contentDefinition = definition; 466 m_encoding = CmsEncoder.lookupEncoding(encoding, encoding); 467 m_elementLocales = new HashMap<String, Set<Locale>>(); 468 m_elementNames = new HashMap<Locale, Set<String>>(); 469 m_locales = new HashSet<Locale>(); 470 m_cntPages = new LinkedHashMap<Locale, CmsContainerPageBean>(); 471 clearBookmarks(); 472 473 // initialize the bookmarks 474 for (Iterator<Element> itCntPages = CmsXmlGenericWrapper.elementIterator( 475 m_document.getRootElement()); itCntPages.hasNext();) { 476 Element cntPage = itCntPages.next(); 477 478 try { 479 Locale locale = CmsLocaleManager.getLocale( 480 cntPage.attribute(CmsXmlContentDefinition.XSD_ATTRIBUTE_VALUE_LANGUAGE).getValue()); 481 482 addLocale(locale); 483 484 List<CmsContainerBean> containers = new ArrayList<CmsContainerBean>(); 485 for (Iterator<Element> itCnts = CmsXmlGenericWrapper.elementIterator( 486 cntPage, 487 XmlNode.Containers.name()); itCnts.hasNext();) { 488 Element container = itCnts.next(); 489 490 // container itself 491 int cntIndex = CmsXmlUtils.getXpathIndexInt(container.getUniquePath(cntPage)); 492 String cntPath = CmsXmlUtils.createXpathElement(container.getName(), cntIndex); 493 I_CmsXmlSchemaType cntSchemaType = definition.getSchemaType(container.getName()); 494 I_CmsXmlContentValue cntValue = cntSchemaType.createValue(this, container, locale); 495 addBookmark(cntPath, locale, true, cntValue); 496 CmsXmlContentDefinition cntDef = ((CmsXmlNestedContentDefinition)cntSchemaType).getNestedContentDefinition(); 497 498 // name 499 Element name = container.element(XmlNode.Name.name()); 500 addBookmarkForElement(name, locale, container, cntPath, cntDef); 501 502 // type 503 Element type = container.element(XmlNode.Type.name()); 504 addBookmarkForElement(type, locale, container, cntPath, cntDef); 505 506 // parent instance id 507 Element parentInstance = container.element(XmlNode.ParentInstanceId.name()); 508 if (parentInstance != null) { 509 addBookmarkForElement(parentInstance, locale, container, cntPath, cntDef); 510 } 511 512 Element isRootContainer = container.element(XmlNode.IsRootContainer.name()); 513 if (isRootContainer != null) { 514 addBookmarkForElement(isRootContainer, locale, container, cntPath, cntDef); 515 } 516 517 List<CmsContainerElementBean> elements = new ArrayList<CmsContainerElementBean>(); 518 // Elements 519 for (Iterator<Element> itElems = CmsXmlGenericWrapper.elementIterator( 520 container, 521 XmlNode.Elements.name()); itElems.hasNext();) { 522 Element element = itElems.next(); 523 524 // element itself 525 int elemIndex = CmsXmlUtils.getXpathIndexInt(element.getUniquePath(container)); 526 String elemPath = CmsXmlUtils.concatXpath( 527 cntPath, 528 CmsXmlUtils.createXpathElement(element.getName(), elemIndex)); 529 I_CmsXmlSchemaType elemSchemaType = cntDef.getSchemaType(element.getName()); 530 I_CmsXmlContentValue elemValue = elemSchemaType.createValue(this, element, locale); 531 addBookmark(elemPath, locale, true, elemValue); 532 CmsXmlContentDefinition elemDef = ((CmsXmlNestedContentDefinition)elemSchemaType).getNestedContentDefinition(); 533 534 // uri 535 Element uri = element.element(XmlNode.Uri.name()); 536 addBookmarkForElement(uri, locale, element, elemPath, elemDef); 537 Element uriLink = uri.element(CmsXmlPage.NODE_LINK); 538 CmsUUID elementId = null; 539 if (uriLink == null) { 540 // this can happen when adding the elements node to the xml content 541 // it is not dangerous since the link has to be set before saving 542 } else { 543 CmsLink link = new CmsLink(uriLink); 544 if (cms != null) { 545 link.checkConsistency(cms); 546 } 547 elementId = link.getStructureId(); 548 } 549 Element createNewElement = element.element(XmlNode.CreateNew.name()); 550 boolean createNew = (createNewElement != null) 551 && Boolean.parseBoolean(createNewElement.getStringValue()); 552 553 // formatter 554 Element formatter = element.element(XmlNode.Formatter.name()); 555 addBookmarkForElement(formatter, locale, element, elemPath, elemDef); 556 Element formatterLink = formatter.element(CmsXmlPage.NODE_LINK); 557 CmsUUID formatterId = null; 558 if (formatterLink == null) { 559 // this can happen when adding the elements node to the xml content 560 // it is not dangerous since the link has to be set before saving 561 } else { 562 CmsLink link = new CmsLink(formatterLink); 563 if (cms != null) { 564 link.checkConsistency(cms); 565 } 566 formatterId = link.getStructureId(); 567 } 568 569 // the properties 570 Map<String, String> propertiesMap = CmsXmlContentPropertyHelper.readProperties( 571 this, 572 locale, 573 element, 574 elemPath, 575 elemDef); 576 577 if (elementId != null) { 578 elements.add(new CmsContainerElementBean(elementId, formatterId, propertiesMap, createNew)); 579 } 580 } 581 CmsContainerBean newContainerBean = new CmsContainerBean( 582 name.getText(), 583 type.getText(), 584 parentInstance != null ? parentInstance.getText() : null, 585 (isRootContainer != null) && Boolean.valueOf(isRootContainer.getText()).booleanValue(), 586 elements); 587 containers.add(newContainerBean); 588 } 589 590 m_cntPages.put(locale, new CmsContainerPageBean(containers)); 591 } catch (NullPointerException e) { 592 LOG.error( 593 org.opencms.xml.content.Messages.get().getBundle().key( 594 org.opencms.xml.content.Messages.LOG_XMLCONTENT_INIT_BOOKMARKS_0), 595 e); 596 } 597 } 598 599 if (cms != null) { 600 // this will remove all invalid links 601 getHandler().invalidateBrokenLinks(cms, this); 602 } 603 } 604 605 /** 606 * @see org.opencms.xml.A_CmsXmlDocument#initDocument(org.dom4j.Document, java.lang.String, org.opencms.xml.CmsXmlContentDefinition) 607 */ 608 @Override 609 protected void initDocument(Document document, String encoding, CmsXmlContentDefinition definition) { 610 611 initDocument(null, document, encoding, definition); 612 } 613 614 /** 615 * Removes all empty containers to clean up container page XML.<p> 616 * 617 * @param cntPage the container page bean 618 * 619 * @return the newly generated result 620 */ 621 protected CmsContainerPageBean removeEmptyContainers(CmsContainerPageBean cntPage) { 622 623 List<CmsContainerBean> containers = new ArrayList<CmsContainerBean>(); 624 for (CmsContainerBean container : cntPage.getContainers().values()) { 625 if (container.getElements().size() > 0) { 626 containers.add(container); 627 } 628 } 629 return new CmsContainerPageBean(containers); 630 } 631 632 /** 633 * Adds the given container page to the given element.<p> 634 * 635 * @param cms the current CMS object 636 * @param parent the element to add it 637 * @param cntPage the container page to add 638 * 639 * @throws CmsException if something goes wrong 640 */ 641 protected void saveContainerPage(CmsObject cms, Element parent, CmsContainerPageBean cntPage) throws CmsException { 642 643 parent.clearContent(); 644 645 // save containers in a defined order 646 List<String> containerNames = new ArrayList<String>(cntPage.getNames()); 647 Collections.sort(containerNames); 648 649 for (String containerName : containerNames) { 650 CmsContainerBean container = cntPage.getContainers().get(containerName); 651 652 // the container 653 Element cntElement = parent.addElement(XmlNode.Containers.name()); 654 cntElement.addElement(XmlNode.Name.name()).addCDATA(container.getName()); 655 cntElement.addElement(XmlNode.Type.name()).addCDATA(container.getType()); 656 if (container.isNestedContainer()) { 657 cntElement.addElement(XmlNode.ParentInstanceId.name()).addCDATA(container.getParentInstanceId()); 658 } 659 if (container.isRootContainer()) { 660 cntElement.addElement(XmlNode.IsRootContainer.name()).addText(Boolean.TRUE.toString()); 661 } 662 663 // the elements 664 for (CmsContainerElementBean element : container.getElements()) { 665 Element elemElement = cntElement.addElement(XmlNode.Elements.name()); 666 667 // the element 668 Element uriElem = elemElement.addElement(XmlNode.Uri.name()); 669 CmsResource uriRes = fillResource(cms, uriElem, element.getId()); 670 Element formatterElem = elemElement.addElement(XmlNode.Formatter.name()); 671 fillResource(cms, formatterElem, element.getFormatterId()); 672 if (element.isCreateNew()) { 673 Element createNewElem = elemElement.addElement(XmlNode.CreateNew.name()); 674 createNewElem.addText(Boolean.TRUE.toString()); 675 } 676 // the properties 677 Map<String, String> properties = element.getIndividualSettings(); 678 Map<String, CmsXmlContentProperty> propertiesConf = OpenCms.getADEManager().getElementSettings( 679 cms, 680 uriRes); 681 682 CmsXmlContentPropertyHelper.saveProperties(cms, elemElement, properties, propertiesConf); 683 } 684 } 685 } 686 687 /** 688 * @see org.opencms.xml.content.CmsXmlContent#setFile(org.opencms.file.CmsFile) 689 */ 690 @Override 691 protected void setFile(CmsFile file) { 692 693 // just for visibility from the factory 694 super.setFile(file); 695 } 696 697}