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.importexport; 029 030import org.opencms.db.CmsDefaultUsers; 031import org.opencms.db.CmsResourceState; 032import org.opencms.file.CmsFile; 033import org.opencms.file.CmsFolder; 034import org.opencms.file.CmsGroup; 035import org.opencms.file.CmsObject; 036import org.opencms.file.CmsProject; 037import org.opencms.file.CmsProperty; 038import org.opencms.file.CmsPropertyDefinition; 039import org.opencms.file.CmsResource; 040import org.opencms.file.CmsResourceFilter; 041import org.opencms.file.CmsUser; 042import org.opencms.file.CmsVfsException; 043import org.opencms.file.CmsVfsResourceNotFoundException; 044import org.opencms.file.types.CmsResourceTypePlain; 045import org.opencms.i18n.CmsMessageContainer; 046import org.opencms.importexport.CmsImportExportManager.TimestampMode; 047import org.opencms.main.CmsEvent; 048import org.opencms.main.CmsException; 049import org.opencms.main.CmsLog; 050import org.opencms.main.I_CmsEventListener; 051import org.opencms.main.OpenCms; 052import org.opencms.module.CmsModule.ExportMode; 053import org.opencms.relations.CmsRelation; 054import org.opencms.relations.CmsRelationFilter; 055import org.opencms.report.I_CmsReport; 056import org.opencms.security.CmsAccessControlEntry; 057import org.opencms.security.CmsOrganizationalUnit; 058import org.opencms.security.CmsRole; 059import org.opencms.security.CmsRoleViolationException; 060import org.opencms.util.CmsDataTypeUtil; 061import org.opencms.util.CmsDateUtil; 062import org.opencms.util.CmsFileUtil; 063import org.opencms.util.CmsMacroResolver; 064import org.opencms.util.CmsStringUtil; 065import org.opencms.util.CmsUUID; 066import org.opencms.util.CmsXmlSaxWriter; 067import org.opencms.workplace.CmsWorkplace; 068 069import java.io.IOException; 070import java.util.ArrayList; 071import java.util.Collections; 072import java.util.HashMap; 073import java.util.HashSet; 074import java.util.Iterator; 075import java.util.List; 076import java.util.Map; 077import java.util.Set; 078import java.util.stream.Collectors; 079 080import org.apache.commons.codec.binary.Base64; 081import org.apache.commons.logging.Log; 082 083import org.dom4j.Document; 084import org.dom4j.DocumentHelper; 085import org.dom4j.Element; 086import org.dom4j.io.SAXWriter; 087import org.xml.sax.SAXException; 088 089/** 090 * Provides the functionality to export files from the OpenCms VFS to a ZIP file.<p> 091 * 092 * The ZIP file written will contain a copy of all exported files with their contents. 093 * It will also contain a <code>manifest.xml</code> file in which all meta-information 094 * about this files are stored, like permissions etc.<p> 095 * 096 * @since 6.0.0 097 */ 098public class CmsExport { 099 100 /** The log object for this class. */ 101 private static final Log LOG = CmsLog.getLog(CmsExport.class); 102 103 /** The cms context. */ 104 private CmsObject m_cms; 105 106 /** Counter for the export. */ 107 private int m_exportCount; 108 109 /** Set of all exported files, required for preventing redundant sibling export. */ 110 private Set<CmsUUID> m_exportedResources; 111 112 /** The export writer. */ 113 private CmsExportHelper m_exportWriter; 114 115 /** The export parameters. */ 116 private CmsExportParameters m_parameters; 117 118 /** The report. */ 119 private I_CmsReport m_report; 120 121 /** The top level file node where all resources are appended to. */ 122 private Element m_resourceNode; 123 124 /** The SAX writer to write the output to. */ 125 private SAXWriter m_saxWriter; 126 127 /** Cache for previously added super folders. */ 128 private List<String> m_superFolders; 129 130 /** 131 * Constructs a new uninitialized export, required for special subclass data export.<p> 132 */ 133 public CmsExport() { 134 135 // empty constructor 136 } 137 138 /** 139 * Constructs a new export.<p> 140 * 141 * @param cms the cms context 142 * @param report the report 143 * 144 * @throws CmsRoleViolationException if the current user has not the required role 145 */ 146 public CmsExport(CmsObject cms, I_CmsReport report) 147 throws CmsRoleViolationException { 148 149 m_cms = cms; 150 m_report = report; 151 152 // check if the user has the required permissions 153 OpenCms.getRoleManager().checkRole(getCms(), CmsRole.DATABASE_MANAGER); 154 155 } 156 157 /** 158 * Export the data.<p> 159 * 160 * @param parameters the export parameters 161 * 162 * @throws CmsImportExportException if something goes wrong 163 */ 164 public void exportData(CmsExportParameters parameters) throws CmsImportExportException { 165 166 m_parameters = parameters; 167 m_exportCount = 0; 168 169 // clear all caches 170 getReport().println(Messages.get().container(Messages.RPT_CLEARCACHE_0), I_CmsReport.FORMAT_NOTE); 171 OpenCms.fireCmsEvent(new CmsEvent(I_CmsEventListener.EVENT_CLEAR_CACHES, new HashMap<String, Object>(0))); 172 173 try { 174 Element exportNode = openExportFile(parameters.getExportMode()); 175 176 if (m_parameters.getModuleInfo() != null) { 177 // add the module element 178 exportNode.add(m_parameters.getModuleInfo()); 179 // write the XML 180 digestElement(exportNode, m_parameters.getModuleInfo()); 181 } 182 183 // export account data only if selected 184 if (m_parameters.isExportAccountData()) { 185 Element accountsElement = exportNode.addElement(CmsImportVersion10.N_ACCOUNTS); 186 getSaxWriter().writeOpen(accountsElement); 187 188 exportOrgUnits(accountsElement); 189 190 getSaxWriter().writeClose(accountsElement); 191 exportNode.remove(accountsElement); 192 } 193 194 // export resource data only if selected 195 if (m_parameters.isExportResourceData()) { 196 m_parameters.addAdditionalResources(); 197 exportAllResources(exportNode, m_parameters.getResources()); 198 } 199 200 // export project data only if selected 201 if (m_parameters.isExportProjectData()) { 202 Element projectsElement = exportNode.addElement(CmsImportVersion10.N_PROJECTS); 203 getSaxWriter().writeOpen(projectsElement); 204 205 exportProjects(projectsElement); 206 207 getSaxWriter().writeClose(projectsElement); 208 exportNode.remove(projectsElement); 209 } 210 211 closeExportFile(exportNode); 212 } catch (SAXException se) { 213 getReport().println(se); 214 215 CmsMessageContainer message = Messages.get().container( 216 Messages.ERR_IMPORTEXPORT_ERROR_EXPORTING_TO_FILE_1, 217 getExportFileName()); 218 if (LOG.isDebugEnabled()) { 219 LOG.debug(message.key(), se); 220 } 221 222 throw new CmsImportExportException(message, se); 223 } catch (IOException ioe) { 224 getReport().println(ioe); 225 226 CmsMessageContainer message = Messages.get().container( 227 Messages.ERR_IMPORTEXPORT_ERROR_EXPORTING_TO_FILE_1, 228 getExportFileName()); 229 if (LOG.isDebugEnabled()) { 230 LOG.debug(message.key(), ioe); 231 } 232 233 throw new CmsImportExportException(message, ioe); 234 } finally { 235 if (m_exportWriter != null) { 236 m_exportWriter.ensureZipStreamClosed(); 237 } 238 } 239 } 240 241 /** 242 * Exports the given folder and all child resources.<p> 243 * 244 * @param folderName to complete path to the resource to export 245 * 246 * @throws CmsImportExportException if something goes wrong 247 * @throws SAXException if something goes wrong processing the manifest.xml 248 * @throws IOException if not all resources could be appended to the ZIP archive 249 */ 250 protected void addChildResources(String folderName) throws CmsImportExportException, IOException, SAXException { 251 252 try { 253 // get all subFolders 254 List<CmsResource> subFolders = getCms().getSubFolders(folderName, CmsResourceFilter.IGNORE_EXPIRATION); 255 // get all files in folder 256 List<CmsResource> subFiles = getCms().getFilesInFolder(folderName, CmsResourceFilter.IGNORE_EXPIRATION); 257 258 // walk through all files and export them 259 for (int i = 0; i < subFiles.size(); i++) { 260 CmsResource file = subFiles.get(i); 261 CmsResourceState state = file.getState(); 262 long age = file.getDateLastModified() < file.getDateCreated() 263 ? file.getDateCreated() 264 : file.getDateLastModified(); 265 266 if (getCms().getRequestContext().getCurrentProject().isOnlineProject() 267 || (m_parameters.isIncludeUnchangedResources()) 268 || state.isNew() 269 || state.isChanged()) { 270 if (!state.isDeleted() 271 && !CmsWorkplace.isTemporaryFile(file) 272 && (age >= m_parameters.getContentAge())) { 273 String export = getCms().getSitePath(file); 274 if (checkExportResource(export)) { 275 if (isInExportableProject(file)) { 276 exportFile(getCms().readFile(export, CmsResourceFilter.IGNORE_EXPIRATION)); 277 } 278 } 279 } 280 } 281 // release file header memory 282 subFiles.set(i, null); 283 } 284 // all files are exported, release memory 285 subFiles = null; 286 287 // walk through all subfolders and export them 288 for (int i = 0; i < subFolders.size(); i++) { 289 CmsResource folder = subFolders.get(i); 290 if (folder.getState() != CmsResource.STATE_DELETED) { 291 // check if this is a system-folder and if it should be included. 292 String export = getCms().getSitePath(folder); 293 if (checkExportResource(export)) { 294 295 long age = folder.getDateLastModified() < folder.getDateCreated() 296 ? folder.getDateCreated() 297 : folder.getDateLastModified(); 298 // export this folder only if age is above selected age 299 // default for selected age (if not set by user) is <code>long 0</code> (i.e. 1970) 300 if (age >= m_parameters.getContentAge()) { 301 // only export folder data to manifest.xml if it has changed 302 appendResourceToManifest(folder, false); 303 } 304 305 // export all sub-resources in this folder 306 addChildResources(getCms().getSitePath(folder)); 307 } 308 } 309 // release folder memory 310 subFolders.set(i, null); 311 } 312 } catch (CmsImportExportException e) { 313 314 throw e; 315 } catch (CmsException e) { 316 317 CmsMessageContainer message = Messages.get().container( 318 Messages.ERR_IMPORTEXPORT_ERROR_ADDING_CHILD_RESOURCES_1, 319 folderName); 320 if (LOG.isDebugEnabled()) { 321 LOG.debug(message.key(), e); 322 } 323 324 throw new CmsImportExportException(message, e); 325 } 326 } 327 328 /** 329 * Adds all files in fileNames to the manifest.xml file.<p> 330 * 331 * @param fileNames list of path Strings, e.g. <code>/folder/index.html</code> 332 * 333 * @throws CmsImportExportException if something goes wrong 334 * @throws IOException if a file could not be exported 335 * @throws SAXException if something goes wrong processing the manifest.xml 336 */ 337 protected void addFiles(List<String> fileNames) throws CmsImportExportException, IOException, SAXException { 338 339 if (fileNames != null) { 340 for (int i = 0; i < fileNames.size(); i++) { 341 String fileName = fileNames.get(i); 342 343 try { 344 CmsFile file = getCms().readFile(fileName, CmsResourceFilter.IGNORE_EXPIRATION); 345 if (!file.getState().isDeleted() && !CmsWorkplace.isTemporaryFile(file)) { 346 if (checkExportResource(fileName)) { 347 if (m_parameters.isRecursive()) { 348 addParentFolders(fileName); 349 } 350 if (isInExportableProject(file)) { 351 exportFile(file); 352 } 353 } 354 } 355 } catch (CmsImportExportException e) { 356 357 throw e; 358 } catch (CmsException e) { 359 if (e instanceof CmsVfsException) { // file not found 360 CmsMessageContainer message = Messages.get().container( 361 Messages.ERR_IMPORTEXPORT_ERROR_ADDING_FILE_1, 362 fileName); 363 if (LOG.isDebugEnabled()) { 364 LOG.debug(message.key(), e); 365 } 366 367 throw new CmsImportExportException(message, e); 368 } 369 } 370 } 371 } 372 } 373 374 /** 375 * Adds the parent folders of the given resource to the config file, 376 * starting at the top, excluding the root folder.<p> 377 * 378 * @param resourceName the name of a resource in the VFS 379 * 380 * @throws CmsImportExportException if something goes wrong 381 * @throws SAXException if something goes wrong processing the manifest.xml 382 */ 383 protected void addParentFolders(String resourceName) throws CmsImportExportException, SAXException { 384 385 try { 386 // this is a resource in /system/ folder and option includeSystem is not true 387 if (!checkExportResource(resourceName)) { 388 return; 389 } 390 391 // Initialize the "previously added folder cache" 392 if (m_superFolders == null) { 393 m_superFolders = new ArrayList<String>(); 394 } 395 List<String> superFolders = new ArrayList<String>(); 396 String currentSubFolder = resourceName; 397 398 // Check, if the path is really a folder 399 boolean isFolderResource = currentSubFolder.endsWith("/"); 400 401 while (currentSubFolder.length() > "/".length()) { 402 currentSubFolder = currentSubFolder.substring(0, currentSubFolder.length() - 1); 403 currentSubFolder = currentSubFolder.substring(0, currentSubFolder.lastIndexOf("/") + 1); 404 if (currentSubFolder.length() <= "/".length()) { 405 break; 406 } 407 superFolders.add(currentSubFolder); 408 } 409 for (int i = superFolders.size() - 1; i >= 0; i--) { 410 String addFolder = superFolders.get(i); 411 if (!m_superFolders.contains(addFolder)) { 412 // This super folder was NOT added previously. Add it now! 413 CmsFolder folder = getCms().readFolder(addFolder, CmsResourceFilter.IGNORE_EXPIRATION); 414 appendResourceToManifest(folder, false, true); 415 // Remember that this folder was added 416 m_superFolders.add(addFolder); 417 } 418 } 419 if (isFolderResource) { // add the folder itself 420 if (!m_superFolders.contains(resourceName)) { 421 // This super folder was NOT added previously. Add it now! 422 CmsFolder folder = getCms().readFolder(resourceName, CmsResourceFilter.IGNORE_EXPIRATION); 423 appendResourceToManifest(folder, false); 424 // Remember that this folder was added 425 m_superFolders.add(resourceName); 426 } 427 } 428 } catch (CmsImportExportException e) { 429 430 throw e; 431 } catch (CmsException e) { 432 433 CmsMessageContainer message = Messages.get().container( 434 Messages.ERR_IMPORTEXPORT_ERROR_ADDING_PARENT_FOLDERS_1, 435 resourceName); 436 if (LOG.isDebugEnabled()) { 437 LOG.debug(message.key(), e); 438 } 439 440 throw new CmsImportExportException(message, e); 441 } 442 } 443 444 /** 445 * Adds a property node to the manifest.xml.<p> 446 * 447 * @param propertiesElement the parent element to append the node to 448 * @param propertyName the name of the property 449 * @param propertyValue the value of the property 450 * @param shared if <code>true</code>, add a shared property attribute to the generated property node 451 */ 452 protected void addPropertyNode( 453 Element propertiesElement, 454 String propertyName, 455 String propertyValue, 456 boolean shared) { 457 458 if (propertyValue != null) { 459 Element propertyElement = propertiesElement.addElement(CmsImportVersion10.N_PROPERTY); 460 if (shared) { 461 // add "type" attribute to the property node in case of a shared/resource property value 462 propertyElement.addAttribute(CmsImportVersion10.A_TYPE, CmsImportVersion10.PROPERTY_ATTRIB_TYPE_SHARED); 463 } 464 propertyElement.addElement(CmsImportVersion10.N_NAME).addText(propertyName); 465 propertyElement.addElement(CmsImportVersion10.N_VALUE).addCDATA(propertyValue); 466 } 467 } 468 469 /** 470 * Adds a relation node to the <code>manifest.xml</code>.<p> 471 * 472 * @param relationsElement the parent element to append the node to 473 * @param structureId the structure id of the target relation 474 * @param sitePath the site path of the target relation 475 * @param relationType the type of the relation 476 */ 477 protected void addRelationNode(Element relationsElement, String structureId, String sitePath, String relationType) { 478 479 if ((sitePath != null) && (relationType != null)) { 480 Element relationElement = relationsElement.addElement(CmsImportVersion10.N_RELATION); 481 if (structureId != null) { 482 relationElement.addElement(CmsImportVersion10.N_ID).addText(structureId); 483 } 484 relationElement.addElement(CmsImportVersion10.N_PATH).addText(sitePath); 485 relationElement.addElement(CmsImportVersion10.N_TYPE).addText(relationType); 486 } 487 } 488 489 /** @see #appendResourceToManifest(CmsResource, boolean, boolean) 490 * @param resource @see #appendResourceToManifest(CmsResource, boolean, boolean) 491 * @param source @see #appendResourceToManifest(CmsResource, boolean, boolean) 492 * @throws CmsImportExportException @see #appendResourceToManifest(CmsResource, boolean, boolean) 493 * @throws SAXException @see #appendResourceToManifest(CmsResource, boolean, boolean) 494 */ 495 protected void appendResourceToManifest(CmsResource resource, boolean source) 496 throws CmsImportExportException, SAXException { 497 498 appendResourceToManifest(resource, source, false); 499 } 500 501 /** 502 * Writes the data for a resource (like access-rights) to the <code>manifest.xml</code> file.<p> 503 * 504 * @param resource the resource to get the data from 505 * @param source flag to show if the source information in the xml file must be written 506 * @param isSuperFolder flag to indicate that the resource is only a super folder of a module resource. 507 * This will prevent exporting uuid and creation date in the reduced export mode. 508 * 509 * @throws CmsImportExportException if something goes wrong 510 * @throws SAXException if something goes wrong processing the manifest.xml 511 */ 512 protected void appendResourceToManifest(CmsResource resource, boolean source, boolean isSuperFolder) 513 throws CmsImportExportException, SAXException { 514 515 if (isSuperFolder && m_parameters.isSkipParentFolders()) { 516 return; 517 } 518 try { 519 // only write <source> if resource is a file 520 String fileName = trimResourceName(getCms().getSitePath(resource)); 521 if (fileName.startsWith("system/orgunits")) { 522 // it is not allowed to export organizational unit resources 523 // export the organizational units instead 524 return; 525 } 526 527 // define the file node 528 Element fileElement = m_resourceNode.addElement(CmsImportVersion10.N_FILE); 529 530 if (resource.isFile()) { 531 if (source) { 532 fileElement.addElement(CmsImportVersion10.N_SOURCE).addText(fileName); 533 } 534 } else { 535 m_exportCount++; 536 I_CmsReport report = getReport(); 537 // output something to the report for the folder 538 report.print( 539 org.opencms.report.Messages.get().container( 540 org.opencms.report.Messages.RPT_SUCCESSION_1, 541 String.valueOf(m_exportCount)), 542 I_CmsReport.FORMAT_NOTE); 543 report.print(Messages.get().container(Messages.RPT_EXPORT_0), I_CmsReport.FORMAT_NOTE); 544 report.print( 545 org.opencms.report.Messages.get().container( 546 org.opencms.report.Messages.RPT_ARGUMENT_1, 547 getCms().getSitePath(resource))); 548 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 549 report.println( 550 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 551 I_CmsReport.FORMAT_OK); 552 553 if (LOG.isInfoEnabled()) { 554 LOG.info( 555 Messages.get().getBundle().key( 556 Messages.LOG_EXPORTING_OK_2, 557 String.valueOf(m_exportCount), 558 getCms().getSitePath(resource))); 559 } 560 } 561 562 boolean isReducedExportMode = m_parameters.getExportMode().equals(ExportMode.REDUCED); 563 boolean isMinimalMetaData = isReducedExportMode && isSuperFolder && exportWithMinimalMetaData(fileName); 564 565 // <destination> 566 fileElement.addElement(CmsImportVersion10.N_DESTINATION).addText(fileName); 567 // <type> 568 Element typeElem = fileElement.addElement(CmsImportVersion10.N_TYPE); 569 String typeName = null; 570 try { 571 typeName = OpenCms.getResourceManager().getResourceType(resource.getTypeId()).getTypeName(); 572 } catch (CmsException e) { 573 LOG.warn(e.getLocalizedMessage(), e); 574 typeName = "unknown_" + resource.getTypeId(); 575 } 576 typeElem.addText(typeName); 577 578 if (!isMinimalMetaData) { 579 // <uuidstructure> 580 fileElement.addElement(CmsImportVersion10.N_UUIDSTRUCTURE).addText( 581 resource.getStructureId().toString()); 582 if (resource.isFile()) { 583 // <uuidresource> 584 fileElement.addElement(CmsImportVersion10.N_UUIDRESOURCE).addText( 585 resource.getResourceId().toString()); 586 } 587 } 588 589 if (!isReducedExportMode) { 590 // <datelastmodified> 591 fileElement.addElement(CmsImportVersion10.N_DATELASTMODIFIED).addText( 592 getDateLastModifiedForExport(resource)); 593 // <userlastmodified> 594 String userNameLastModified = null; 595 try { 596 userNameLastModified = getCms().readUser(resource.getUserLastModified()).getName(); 597 } catch (@SuppressWarnings("unused") CmsException e) { 598 userNameLastModified = OpenCms.getDefaultUsers().getUserAdmin(); 599 } 600 fileElement.addElement(CmsImportVersion10.N_USERLASTMODIFIED).addText(userNameLastModified); 601 } 602 if (!isMinimalMetaData) { 603 // <datecreated> 604 fileElement.addElement(CmsImportVersion10.N_DATECREATED).addText( 605 CmsDateUtil.getHeaderDate(resource.getDateCreated())); 606 } 607 if (!isReducedExportMode) { 608 // <usercreated> 609 String userNameCreated = null; 610 try { 611 userNameCreated = getCms().readUser(resource.getUserCreated()).getName(); 612 } catch (@SuppressWarnings("unused") CmsException e) { 613 userNameCreated = OpenCms.getDefaultUsers().getUserAdmin(); 614 } 615 fileElement.addElement(CmsImportVersion10.N_USERCREATED).addText(userNameCreated); 616 } 617 if (!isMinimalMetaData) { 618 // <release> 619 if (resource.getDateReleased() != CmsResource.DATE_RELEASED_DEFAULT) { 620 fileElement.addElement(CmsImportVersion10.N_DATERELEASED).addText( 621 CmsDateUtil.getHeaderDate(resource.getDateReleased())); 622 } 623 // <expire> 624 if (resource.getDateExpired() != CmsResource.DATE_EXPIRED_DEFAULT) { 625 fileElement.addElement(CmsImportVersion10.N_DATEEXPIRED).addText( 626 CmsDateUtil.getHeaderDate(resource.getDateExpired())); 627 } 628 // <flags> 629 int resFlags = resource.getFlags(); 630 resFlags &= ~CmsResource.FLAG_LABELED; 631 fileElement.addElement(CmsImportVersion10.N_FLAGS).addText(Integer.toString(resFlags)); 632 633 // write the properties to the manifest 634 Element propertiesElement = fileElement.addElement(CmsImportVersion10.N_PROPERTIES); 635 List<CmsProperty> properties = getCms().readPropertyObjects(getCms().getSitePath(resource), false); 636 CmsProperty exportTypeProp = CmsProperty.get(CmsPropertyDefinition.PROPERTY_EXPORT_TYPE, properties); 637 String exportType = exportTypeProp.getValue(); 638 if ((exportType != null) && CmsResourceTypePlain.getStaticTypeName().equals(typeElem.getText())) { 639 typeElem.setText(exportType); 640 } 641 // sort the properties for a well defined output order 642 Collections.sort(properties); 643 for (int i = 0, n = properties.size(); i < n; i++) { 644 CmsProperty property = properties.get(i); 645 if (isIgnoredProperty(property)) { 646 continue; 647 } 648 addPropertyNode(propertiesElement, property.getName(), property.getStructureValue(), false); 649 addPropertyNode(propertiesElement, property.getName(), property.getResourceValue(), true); 650 } 651 652 // Write the relations to the manifest 653 List<CmsRelation> relations = getCms().getRelationsForResource( 654 resource, 655 CmsRelationFilter.TARGETS.filterNotDefinedInContent()); 656 Element relationsElement = fileElement.addElement(CmsImportVersion10.N_RELATIONS); 657 // iterate over the relations 658 for (CmsRelation relation : relations) { 659 // relation may be broken already: 660 try { 661 CmsResource target = relation.getTarget(getCms(), CmsResourceFilter.ALL); 662 String structureId = target.getStructureId().toString(); 663 String sitePath = getCms().getSitePath(target); 664 String relationType = relation.getType().getName(); 665 if (OpenCms.getImportExportManager().isImmutable(target.getRootPath())) { 666 structureId = null; 667 } 668 addRelationNode(relationsElement, structureId, sitePath, relationType); 669 } catch (CmsVfsResourceNotFoundException crnfe) { 670 // skip this relation: 671 if (LOG.isWarnEnabled()) { 672 LOG.warn( 673 Messages.get().getBundle().key( 674 Messages.LOG_IMPORTEXPORT_WARN_DELETED_RELATIONS_2, 675 new String[] {relation.getTargetPath(), resource.getRootPath()}), 676 crnfe); 677 } 678 } 679 } 680 681 // append the nodes for access control entries 682 Element acl = fileElement.addElement(CmsImportVersion10.N_ACCESSCONTROL_ENTRIES); 683 684 // read the access control entries 685 List<CmsAccessControlEntry> fileAcEntries = getCms().getAccessControlEntries( 686 getCms().getSitePath(resource), 687 false); 688 Iterator<CmsAccessControlEntry> i = fileAcEntries.iterator(); 689 690 // create xml elements for each access control entry 691 while (i.hasNext()) { 692 CmsAccessControlEntry ace = i.next(); 693 Element a = acl.addElement(CmsImportVersion10.N_ACCESSCONTROL_ENTRY); 694 695 // now check if the principal is a group or a user 696 int flags = ace.getFlags(); 697 String acePrincipalName = ""; 698 CmsUUID acePrincipal = ace.getPrincipal(); 699 if ((flags & CmsAccessControlEntry.ACCESS_FLAGS_ALLOTHERS) > 0) { 700 acePrincipalName = CmsAccessControlEntry.PRINCIPAL_ALL_OTHERS_NAME; 701 } else if ((flags & CmsAccessControlEntry.ACCESS_FLAGS_OVERWRITE_ALL) > 0) { 702 acePrincipalName = CmsAccessControlEntry.PRINCIPAL_OVERWRITE_ALL_NAME; 703 } else if ((flags & CmsAccessControlEntry.ACCESS_FLAGS_GROUP) > 0) { 704 // the principal is a group 705 try { 706 acePrincipalName = getCms().readGroup(acePrincipal).getPrefixedName(); 707 } catch (@SuppressWarnings("unused") CmsException e) { 708 // the group for this permissions does not exist anymore, so simply skip it 709 } 710 } else if ((flags & CmsAccessControlEntry.ACCESS_FLAGS_USER) > 0) { 711 // the principal is a user 712 try { 713 acePrincipalName = getCms().readUser(acePrincipal).getPrefixedName(); 714 } catch (@SuppressWarnings("unused") CmsException e) { 715 // the user for this permissions does not exist anymore, so simply skip it 716 } 717 } else { 718 // the principal is a role 719 acePrincipalName = CmsRole.PRINCIPAL_ROLE + "." + CmsRole.valueOfId(acePrincipal).getRoleName(); 720 } 721 722 // only add the permission if a principal was set 723 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(acePrincipalName)) { 724 a.addElement(CmsImportVersion10.N_ACCESSCONTROL_PRINCIPAL).addText(acePrincipalName); 725 a.addElement(CmsImportVersion10.N_FLAGS).addText(Integer.toString(flags)); 726 727 Element b = a.addElement(CmsImportVersion10.N_ACCESSCONTROL_PERMISSIONSET); 728 b.addElement(CmsImportVersion10.N_ACCESSCONTROL_ALLOWEDPERMISSIONS).addText( 729 Integer.toString(ace.getAllowedPermissions())); 730 b.addElement(CmsImportVersion10.N_ACCESSCONTROL_DENIEDPERMISSIONS).addText( 731 Integer.toString(ace.getDeniedPermissions())); 732 } 733 } 734 } else { 735 fileElement.addElement(CmsImportVersion10.N_PROPERTIES); 736 } 737 738 // write the XML 739 digestElement(m_resourceNode, fileElement); 740 } catch (CmsImportExportException e) { 741 742 throw e; 743 } catch (CmsException e) { 744 745 CmsMessageContainer message = Messages.get().container( 746 Messages.ERR_IMPORTEXPORT_ERROR_APPENDING_RESOURCE_TO_MANIFEST_1, 747 resource.getRootPath()); 748 if (LOG.isDebugEnabled()) { 749 LOG.debug(message.key(), e); 750 } 751 752 throw new CmsImportExportException(message, e); 753 } 754 } 755 756 /** 757 * Returns true if the checked resource name can be exported depending on the include settings.<p> 758 * 759 * @param resourcename the absolute path of the resource 760 * @return true if the checked resource name can be exported depending on the include settings 761 */ 762 protected boolean checkExportResource(String resourcename) { 763 764 return (// other folder than "/system/" will be exported 765 !resourcename.startsWith(CmsWorkplace.VFS_PATH_SYSTEM) // OR always export "/system/" 766 || resourcename.equalsIgnoreCase(CmsWorkplace.VFS_PATH_SYSTEM) // OR always export "/system/galleries/" 767 || resourcename.startsWith(CmsWorkplace.VFS_PATH_GALLERIES) // OR option "include system folder" selected 768 || (m_parameters.isIncludeSystemFolder() // AND export folder is a system folder 769 && resourcename.startsWith(CmsWorkplace.VFS_PATH_SYSTEM))); 770 } 771 772 /** 773 * Closes the export ZIP file and saves the XML document for the manifest.<p> 774 * 775 * @param exportNode the export root node 776 * 777 * @throws SAXException if something goes wrong processing the manifest.xml 778 * @throws IOException if something goes wrong while closing the export file 779 */ 780 protected void closeExportFile(Element exportNode) throws IOException, SAXException { 781 782 // close the <export> Tag 783 getSaxWriter().writeClose(exportNode); 784 785 // close the XML document 786 CmsXmlSaxWriter xmlSaxWriter = (CmsXmlSaxWriter)getSaxWriter().getContentHandler(); 787 788 // write the manifest file 789 m_exportWriter.writeManifest(xmlSaxWriter); 790 } 791 792 /** 793 * Writes the output element to the XML output writer and detaches it 794 * from it's parent element.<p> 795 * 796 * @param parent the parent element 797 * @param output the output element 798 * 799 * @throws SAXException if something goes wrong processing the manifest.xml 800 */ 801 protected void digestElement(Element parent, Element output) throws SAXException { 802 803 m_saxWriter.write(output); 804 parent.remove(output); 805 } 806 807 /** 808 * Exports all resources and possible sub-folders form the provided list of resources. 809 * 810 * @param parent the parent node to add the resources to 811 * @param resourcesToExport the list of resources to export 812 * 813 * @throws CmsImportExportException if something goes wrong 814 * @throws SAXException if something goes wrong processing the manifest.xml 815 * @throws IOException if not all resources could be appended to the ZIP archive 816 */ 817 protected void exportAllResources(Element parent, List<String> resourcesToExport) 818 throws CmsImportExportException, IOException, SAXException { 819 820 // export all the resources 821 String resourceNodeName = getResourceNodeName(); 822 m_resourceNode = parent.addElement(resourceNodeName); 823 getSaxWriter().writeOpen(m_resourceNode); 824 825 if (m_parameters.isRecursive()) { 826 String siteRoot = m_cms.getRequestContext().getSiteRoot(); 827 if (siteRoot.equals("") || siteRoot.equals("/")) { 828 resourcesToExport = CmsFileUtil.removeRedundancies(resourcesToExport); 829 } else { 830 // Prevent resources in /system or other sites from being removed when '/' for current site is also selected 831 Map<String, String> rootToSitePaths = new HashMap<>(); 832 for (String sitePath : resourcesToExport) { 833 String rootPath = m_cms.addSiteRoot(sitePath); 834 rootToSitePaths.put(rootPath, sitePath); 835 } 836 resourcesToExport = CmsFileUtil.removeRedundancies( 837 new ArrayList<>(rootToSitePaths.keySet())).stream().map(rootToSitePaths::get).collect( 838 Collectors.toList()); 839 840 } 841 } 842 843 // distinguish folder and file names 844 List<String> folderNames = new ArrayList<String>(); 845 List<String> fileNames = new ArrayList<String>(); 846 Iterator<String> it = resourcesToExport.iterator(); 847 while (it.hasNext()) { 848 String resource = it.next(); 849 if (CmsResource.isFolder(resource)) { 850 folderNames.add(resource); 851 } else { 852 fileNames.add(resource); 853 } 854 } 855 856 m_exportedResources = new HashSet<CmsUUID>(); 857 858 // export the folders 859 for (int i = 0; i < folderNames.size(); i++) { 860 String path = folderNames.get(i); 861 if (m_parameters.isRecursive()) { 862 // first add super folders to the xml-config file 863 addParentFolders(path); 864 addChildResources(path); 865 } else { 866 CmsFolder folder; 867 try { 868 folder = getCms().readFolder(path, CmsResourceFilter.IGNORE_EXPIRATION); 869 } catch (CmsException e) { 870 CmsMessageContainer message = Messages.get().container( 871 Messages.ERR_IMPORTEXPORT_ERROR_ADDING_PARENT_FOLDERS_1, 872 path); 873 if (LOG.isDebugEnabled()) { 874 LOG.debug(message.key(), e); 875 } 876 throw new CmsImportExportException(message, e); 877 } 878 CmsResourceState state = folder.getState(); 879 long age = folder.getDateLastModified() < folder.getDateCreated() 880 ? folder.getDateCreated() 881 : folder.getDateLastModified(); 882 883 if (getCms().getRequestContext().getCurrentProject().isOnlineProject() 884 || (m_parameters.isIncludeUnchangedResources()) 885 || state.isNew() 886 || state.isChanged()) { 887 if (!state.isDeleted() && (age >= m_parameters.getContentAge())) { 888 // check if this is a system-folder and if it should be included. 889 String export = getCms().getSitePath(folder); 890 if (checkExportResource(export)) { 891 appendResourceToManifest(folder, true); 892 } 893 } 894 } 895 } 896 } 897 // export the files 898 addFiles(fileNames); 899 900 // write the XML 901 getSaxWriter().writeClose(m_resourceNode); 902 parent.remove(m_resourceNode); 903 m_resourceNode = null; 904 } 905 906 /** 907 * Exports one single file with all its data and content.<p> 908 * 909 * @param file the file to be exported 910 * 911 * @throws CmsImportExportException if something goes wrong 912 * @throws SAXException if something goes wrong processing the manifest.xml 913 * @throws IOException if the ZIP entry for the file could be appended to the ZIP archive 914 */ 915 protected void exportFile(CmsFile file) throws CmsImportExportException, SAXException, IOException { 916 917 String source = trimResourceName(getCms().getSitePath(file)); 918 I_CmsReport report = getReport(); 919 m_exportCount++; 920 report.print( 921 org.opencms.report.Messages.get().container( 922 org.opencms.report.Messages.RPT_SUCCESSION_1, 923 String.valueOf(m_exportCount)), 924 I_CmsReport.FORMAT_NOTE); 925 report.print(Messages.get().container(Messages.RPT_EXPORT_0), I_CmsReport.FORMAT_NOTE); 926 report.print( 927 org.opencms.report.Messages.get().container( 928 org.opencms.report.Messages.RPT_ARGUMENT_1, 929 getCms().getSitePath(file))); 930 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 931 932 // store content in zip-file 933 // check if the content of this resource was not already exported 934 if (!m_exportedResources.contains(file.getResourceId())) { 935 // write the file using the export writer 936 m_exportWriter.writeFile(file, source); 937 // add the resource id to the storage to mark that this resource was already exported 938 m_exportedResources.add(file.getResourceId()); 939 // create the manifest-entries 940 appendResourceToManifest(file, true); 941 } else { 942 // only create the manifest-entries 943 appendResourceToManifest(file, false); 944 } 945 946 if (LOG.isInfoEnabled()) { 947 LOG.info( 948 Messages.get().getBundle().key(Messages.LOG_EXPORTING_OK_2, String.valueOf(m_exportCount), source)); 949 } 950 report.println( 951 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 952 I_CmsReport.FORMAT_OK); 953 } 954 955 /** 956 * Exports one single group with all it's data.<p> 957 * 958 * @param parent the parent node to add the groups to 959 * @param group the group to be exported 960 * 961 * @throws CmsImportExportException if something goes wrong 962 * @throws SAXException if something goes wrong processing the manifest.xml 963 */ 964 protected void exportGroup(Element parent, CmsGroup group) throws CmsImportExportException, SAXException { 965 966 try { 967 String parentgroup; 968 if ((group.getParentId() == null) || group.getParentId().isNullUUID()) { 969 parentgroup = ""; 970 } else { 971 parentgroup = getCms().getParent(group.getName()).getName(); 972 } 973 974 Element e = parent.addElement(CmsImportVersion10.N_GROUP); 975 e.addElement(CmsImportVersion10.N_NAME).addText(group.getSimpleName()); 976 e.addElement(CmsImportVersion10.N_DESCRIPTION).addCDATA(group.getDescription()); 977 e.addElement(CmsImportVersion10.N_FLAGS).addText(Integer.toString(group.getFlags())); 978 e.addElement(CmsImportVersion10.N_PARENTGROUP).addText(parentgroup); 979 980 // write the XML 981 digestElement(parent, e); 982 } catch (CmsException e) { 983 CmsMessageContainer message = org.opencms.db.Messages.get().container( 984 org.opencms.db.Messages.ERR_GET_PARENT_GROUP_1, 985 group.getName()); 986 if (LOG.isDebugEnabled()) { 987 LOG.debug(message.key(), e); 988 } 989 990 throw new CmsImportExportException(message, e); 991 } 992 } 993 994 /** 995 * Exports all groups of the given organizational unit.<p> 996 * 997 * @param parent the parent node to add the groups to 998 * @param orgunit the organizational unit to write the groups for 999 * 1000 * @throws CmsImportExportException if something goes wrong 1001 * @throws SAXException if something goes wrong processing the manifest.xml 1002 */ 1003 protected void exportGroups(Element parent, CmsOrganizationalUnit orgunit) 1004 throws CmsImportExportException, SAXException { 1005 1006 try { 1007 I_CmsReport report = getReport(); 1008 List<CmsGroup> allGroups = OpenCms.getOrgUnitManager().getGroups(getCms(), orgunit.getName(), false); 1009 for (int i = 0, l = allGroups.size(); i < l; i++) { 1010 CmsGroup group = allGroups.get(i); 1011 report.print( 1012 org.opencms.report.Messages.get().container( 1013 org.opencms.report.Messages.RPT_SUCCESSION_2, 1014 String.valueOf(i + 1), 1015 String.valueOf(l)), 1016 I_CmsReport.FORMAT_NOTE); 1017 report.print(Messages.get().container(Messages.RPT_EXPORT_GROUP_0), I_CmsReport.FORMAT_NOTE); 1018 report.print( 1019 org.opencms.report.Messages.get().container( 1020 org.opencms.report.Messages.RPT_ARGUMENT_1, 1021 group.getName())); 1022 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 1023 exportGroup(parent, group); 1024 report.println( 1025 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 1026 I_CmsReport.FORMAT_OK); 1027 } 1028 } catch (CmsImportExportException e) { 1029 throw e; 1030 } catch (CmsException e) { 1031 if (LOG.isDebugEnabled()) { 1032 LOG.debug(e.getLocalizedMessage(), e); 1033 } 1034 throw new CmsImportExportException(e.getMessageContainer(), e); 1035 } 1036 } 1037 1038 /** 1039 * Exports one single organizational unit with all it's data.<p> 1040 * 1041 * @param parent the parent node to add the groups to 1042 * @param orgunit the group to be exported 1043 * 1044 * @throws SAXException if something goes wrong processing the manifest.xml 1045 * @throws CmsException if something goes wrong reading the data to export 1046 */ 1047 protected void exportOrgUnit(Element parent, CmsOrganizationalUnit orgunit) throws SAXException, CmsException { 1048 1049 Element orgunitElement = parent.addElement(CmsImportVersion10.N_ORGUNIT); 1050 getSaxWriter().writeOpen(orgunitElement); 1051 1052 Element name = orgunitElement.addElement(CmsImportVersion10.N_NAME).addText(orgunit.getName()); 1053 digestElement(orgunitElement, name); 1054 1055 Element description = orgunitElement.addElement(CmsImportVersion10.N_DESCRIPTION).addCDATA( 1056 orgunit.getDescription()); 1057 digestElement(orgunitElement, description); 1058 1059 Element flags = orgunitElement.addElement(CmsImportVersion10.N_FLAGS).addText( 1060 Integer.toString(orgunit.getFlags())); 1061 digestElement(orgunitElement, flags); 1062 1063 Element resources = orgunitElement.addElement(CmsImportVersion10.N_RESOURCES); 1064 Iterator<CmsResource> it = OpenCms.getOrgUnitManager().getResourcesForOrganizationalUnit( 1065 getCms(), 1066 orgunit.getName()).iterator(); 1067 while (it.hasNext()) { 1068 CmsResource resource = it.next(); 1069 resources.addElement(CmsImportVersion10.N_RESOURCE).addText(resource.getRootPath()); 1070 } 1071 digestElement(orgunitElement, resources); 1072 getReport().println( 1073 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 1074 I_CmsReport.FORMAT_OK); 1075 1076 Element groupsElement = parent.addElement(CmsImportVersion10.N_GROUPS); 1077 getSaxWriter().writeOpen(groupsElement); 1078 exportGroups(groupsElement, orgunit); 1079 getSaxWriter().writeClose(groupsElement); 1080 1081 Element usersElement = parent.addElement(CmsImportVersion10.N_USERS); 1082 getSaxWriter().writeOpen(usersElement); 1083 exportUsers(usersElement, orgunit); 1084 getSaxWriter().writeClose(usersElement); 1085 1086 getSaxWriter().writeClose(orgunitElement); 1087 } 1088 1089 /** 1090 * Exports all organizational units with all data.<p> 1091 * 1092 * @param parent the parent node to add the organizational units to 1093 * 1094 * @throws CmsImportExportException if something goes wrong 1095 * @throws SAXException if something goes wrong processing the manifest.xml 1096 */ 1097 protected void exportOrgUnits(Element parent) throws CmsImportExportException, SAXException { 1098 1099 try { 1100 Element orgunitsElement = parent.addElement(CmsImportVersion10.N_ORGUNITS); 1101 getSaxWriter().writeOpen(orgunitsElement); 1102 1103 I_CmsReport report = getReport(); 1104 List<CmsOrganizationalUnit> allOUs = new ArrayList<CmsOrganizationalUnit>(); 1105 allOUs.add(OpenCms.getOrgUnitManager().readOrganizationalUnit(getCms(), "")); 1106 allOUs.addAll(OpenCms.getOrgUnitManager().getOrganizationalUnits(getCms(), "", true)); 1107 for (int i = 0; i < allOUs.size(); i++) { 1108 CmsOrganizationalUnit ou = allOUs.get(i); 1109 report.print( 1110 org.opencms.report.Messages.get().container( 1111 org.opencms.report.Messages.RPT_SUCCESSION_2, 1112 String.valueOf(i + 1), 1113 String.valueOf(allOUs.size())), 1114 I_CmsReport.FORMAT_NOTE); 1115 report.print(Messages.get().container(Messages.RPT_EXPORT_ORGUNIT_0), I_CmsReport.FORMAT_NOTE); 1116 report.print( 1117 org.opencms.report.Messages.get().container( 1118 org.opencms.report.Messages.RPT_ARGUMENT_1, 1119 ou.getName())); 1120 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 1121 1122 exportOrgUnit(orgunitsElement, ou); 1123 } 1124 getSaxWriter().writeClose(orgunitsElement); 1125 } catch (CmsImportExportException e) { 1126 throw e; 1127 } catch (CmsException e) { 1128 if (LOG.isDebugEnabled()) { 1129 LOG.debug(e.getLocalizedMessage(), e); 1130 } 1131 throw new CmsImportExportException(e.getMessageContainer(), e); 1132 } 1133 } 1134 1135 /** 1136 * Exports one single project with all it's data.<p> 1137 * 1138 * @param parent the parent node to add the project to 1139 * @param project the project to be exported 1140 * 1141 * @throws CmsImportExportException if something goes wrong 1142 * @throws SAXException if something goes wrong processing the manifest.xml 1143 */ 1144 protected void exportProject(Element parent, CmsProject project) throws CmsImportExportException, SAXException { 1145 1146 I_CmsReport report = getReport(); 1147 CmsDefaultUsers defaultUsers = OpenCms.getDefaultUsers(); 1148 1149 String users; 1150 try { 1151 users = getCms().readGroup(project.getGroupId()).getName(); 1152 } catch (CmsException e) { 1153 CmsMessageContainer message = org.opencms.db.Messages.get().container( 1154 org.opencms.db.Messages.ERR_READ_GROUP_FOR_ID_1, 1155 project.getGroupId()); 1156 if (LOG.isDebugEnabled()) { 1157 LOG.debug(message.key(), e); 1158 } 1159 1160 users = defaultUsers.getGroupUsers(); 1161 report.println(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 1162 report.print(message, I_CmsReport.FORMAT_ERROR); 1163 1164 } 1165 String managers; 1166 try { 1167 managers = getCms().readGroup(project.getManagerGroupId()).getName(); 1168 } catch (CmsException e) { 1169 CmsMessageContainer message = org.opencms.db.Messages.get().container( 1170 org.opencms.db.Messages.ERR_READ_GROUP_FOR_ID_1, 1171 project.getManagerGroupId()); 1172 if (LOG.isDebugEnabled()) { 1173 LOG.debug(message.key(), e); 1174 } 1175 1176 managers = defaultUsers.getGroupAdministrators(); 1177 report.println(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 1178 report.print(message, I_CmsReport.FORMAT_ERROR); 1179 } 1180 1181 Element e = parent.addElement(CmsImportVersion10.N_PROJECT); 1182 e.addElement(CmsImportVersion10.N_NAME).addText(project.getSimpleName()); 1183 e.addElement(CmsImportVersion10.N_DESCRIPTION).addCDATA(project.getDescription()); 1184 e.addElement(CmsImportVersion10.N_USERSGROUP).addText(users); 1185 e.addElement(CmsImportVersion10.N_MANAGERSGROUP).addText(managers); 1186 1187 Element resources = e.addElement(CmsImportVersion10.N_RESOURCES); 1188 try { 1189 Iterator<String> it = getCms().readProjectResources(project).iterator(); 1190 while (it.hasNext()) { 1191 String resName = it.next(); 1192 resources.addElement(CmsImportVersion10.N_RESOURCE).addText(resName); 1193 } 1194 } catch (CmsException exc) { 1195 CmsMessageContainer message = org.opencms.db.Messages.get().container( 1196 org.opencms.db.Messages.ERR_READ_PROJECT_RESOURCES_2, 1197 project.getName(), 1198 project.getUuid()); 1199 if (LOG.isDebugEnabled()) { 1200 LOG.debug(message.key(), exc); 1201 } 1202 1203 throw new CmsImportExportException(message, exc); 1204 } 1205 // write the XML 1206 digestElement(parent, e); 1207 } 1208 1209 /** 1210 * Exports all projects with all data.<p> 1211 * 1212 * @param parent the parent node to add the projects to 1213 * 1214 * @throws CmsImportExportException if something goes wrong 1215 * @throws SAXException if something goes wrong processing the manifest.xml 1216 */ 1217 protected void exportProjects(Element parent) throws CmsImportExportException, SAXException { 1218 1219 try { 1220 I_CmsReport report = getReport(); 1221 List<CmsProject> allProjects = OpenCms.getOrgUnitManager().getAllManageableProjects(getCms(), "", true); 1222 for (int i = 0; i < allProjects.size(); i++) { 1223 CmsProject project = allProjects.get(i); 1224 report.print( 1225 org.opencms.report.Messages.get().container( 1226 org.opencms.report.Messages.RPT_SUCCESSION_2, 1227 String.valueOf(i + 1), 1228 String.valueOf(allProjects.size())), 1229 I_CmsReport.FORMAT_NOTE); 1230 report.print(Messages.get().container(Messages.RPT_EXPORT_PROJECT_0), I_CmsReport.FORMAT_NOTE); 1231 report.print( 1232 org.opencms.report.Messages.get().container( 1233 org.opencms.report.Messages.RPT_ARGUMENT_1, 1234 project.getName())); 1235 1236 exportProject(parent, project); 1237 1238 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 1239 report.println( 1240 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 1241 I_CmsReport.FORMAT_OK); 1242 } 1243 } catch (CmsImportExportException e) { 1244 throw e; 1245 } catch (CmsException e) { 1246 if (LOG.isDebugEnabled()) { 1247 LOG.debug(e.getLocalizedMessage(), e); 1248 } 1249 throw new CmsImportExportException(e.getMessageContainer(), e); 1250 } 1251 } 1252 1253 /** 1254 * Exports one single user with all its data.<p> 1255 * 1256 * @param parent the parent node to add the users to 1257 * @param user the user to be exported 1258 * 1259 * @throws CmsImportExportException if something goes wrong 1260 * @throws SAXException if something goes wrong processing the manifest.xml 1261 */ 1262 protected void exportUser(Element parent, CmsUser user) throws CmsImportExportException, SAXException { 1263 1264 try { 1265 // add user node to the manifest.xml 1266 Element e = parent.addElement(CmsImportVersion10.N_USER); 1267 e.addElement(CmsImportVersion10.N_NAME).addText(user.getSimpleName()); 1268 // encode the password, using a base 64 decoder 1269 String passwd = new String(Base64.encodeBase64(user.getPassword().getBytes())); 1270 e.addElement(CmsImportVersion10.N_PASSWORD).addCDATA(passwd); 1271 e.addElement(CmsImportVersion10.N_FIRSTNAME).addText(user.getFirstname()); 1272 e.addElement(CmsImportVersion10.N_LASTNAME).addText(user.getLastname()); 1273 e.addElement(CmsImportVersion10.N_EMAIL).addText(user.getEmail()); 1274 e.addElement(CmsImportVersion10.N_FLAGS).addText(Integer.toString(user.getFlags())); 1275 e.addElement(CmsImportVersion10.N_DATECREATED).addText(Long.toString(user.getDateCreated())); 1276 1277 Element userInfoNode = e.addElement(CmsImportVersion10.N_USERINFO); 1278 List<String> keys = new ArrayList<String>(user.getAdditionalInfo().keySet()); 1279 Collections.sort(keys); 1280 Iterator<String> itInfoKeys = keys.iterator(); 1281 while (itInfoKeys.hasNext()) { 1282 String key = itInfoKeys.next(); 1283 if (key == null) { 1284 continue; 1285 } 1286 Object value = user.getAdditionalInfo(key); 1287 if (value == null) { 1288 continue; 1289 } 1290 Element entryNode = userInfoNode.addElement(CmsImportVersion10.N_USERINFO_ENTRY); 1291 entryNode.addAttribute(CmsImportVersion10.A_NAME, key); 1292 entryNode.addAttribute(CmsImportVersion10.A_TYPE, value.getClass().getName()); 1293 try { 1294 // serialize the user info and write it into a file 1295 entryNode.addCDATA(CmsDataTypeUtil.dataExport(value)); 1296 } catch (IOException ioe) { 1297 getReport().println(ioe); 1298 if (LOG.isErrorEnabled()) { 1299 LOG.error( 1300 Messages.get().getBundle().key( 1301 Messages.ERR_IMPORTEXPORT_ERROR_EXPORTING_USER_1, 1302 user.getName()), 1303 ioe); 1304 } 1305 } 1306 } 1307 1308 // append node for roles of user 1309 Element userRoles = e.addElement(CmsImportVersion10.N_USERROLES); 1310 List<CmsRole> roles = OpenCms.getRoleManager().getRolesOfUser( 1311 getCms(), 1312 user.getName(), 1313 "", 1314 true, 1315 true, 1316 true); 1317 for (int i = 0; i < roles.size(); i++) { 1318 String roleName = roles.get(i).getFqn(); 1319 userRoles.addElement(CmsImportVersion10.N_USERROLE).addText(roleName); 1320 } 1321 // append the node for groups of user 1322 Element userGroups = e.addElement(CmsImportVersion10.N_USERGROUPS); 1323 List<CmsGroup> groups = getCms().getGroupsOfUser(user.getName(), true, true); 1324 for (int i = 0; i < groups.size(); i++) { 1325 String groupName = groups.get(i).getName(); 1326 userGroups.addElement(CmsImportVersion10.N_USERGROUP).addText(groupName); 1327 } 1328 // write the XML 1329 digestElement(parent, e); 1330 } catch (CmsException e) { 1331 if (LOG.isDebugEnabled()) { 1332 LOG.debug(e.getLocalizedMessage(), e); 1333 } 1334 throw new CmsImportExportException(e.getMessageContainer(), e); 1335 } 1336 } 1337 1338 /** 1339 * Exports all users of the given organizational unit.<p> 1340 * 1341 * @param parent the parent node to add the users to 1342 * @param orgunit the organizational unit to write the groups for 1343 * 1344 * @throws CmsImportExportException if something goes wrong 1345 * @throws SAXException if something goes wrong processing the manifest.xml 1346 */ 1347 protected void exportUsers(Element parent, CmsOrganizationalUnit orgunit) 1348 throws CmsImportExportException, SAXException { 1349 1350 try { 1351 I_CmsReport report = getReport(); 1352 List<CmsUser> allUsers = OpenCms.getOrgUnitManager().getUsers(getCms(), orgunit.getName(), false); 1353 for (int i = 0, l = allUsers.size(); i < l; i++) { 1354 CmsUser user = allUsers.get(i); 1355 report.print( 1356 org.opencms.report.Messages.get().container( 1357 org.opencms.report.Messages.RPT_SUCCESSION_2, 1358 String.valueOf(i + 1), 1359 String.valueOf(l)), 1360 I_CmsReport.FORMAT_NOTE); 1361 report.print(Messages.get().container(Messages.RPT_EXPORT_USER_0), I_CmsReport.FORMAT_NOTE); 1362 report.print( 1363 org.opencms.report.Messages.get().container( 1364 org.opencms.report.Messages.RPT_ARGUMENT_1, 1365 user.getName())); 1366 report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0)); 1367 exportUser(parent, user); 1368 report.println( 1369 org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0), 1370 I_CmsReport.FORMAT_OK); 1371 } 1372 } catch (CmsImportExportException e) { 1373 throw e; 1374 } catch (CmsException e) { 1375 if (LOG.isDebugEnabled()) { 1376 LOG.debug(e.getLocalizedMessage(), e); 1377 } 1378 throw new CmsImportExportException(e.getMessageContainer(), e); 1379 } 1380 } 1381 1382 /** 1383 * Check, if the resource should be exported with minimal meta-data. 1384 * This holds for resources that are not part of the export, but must be 1385 * exported as super-folders. 1386 * 1387 * @param path export-site relative path of the resource to check. 1388 * 1389 * @return flag, indicating if the resource should be exported with minimal meta data. 1390 */ 1391 protected boolean exportWithMinimalMetaData(String path) { 1392 1393 String checkPath = path.startsWith("/") ? path + "/" : "/" + path + "/"; 1394 for (String p : m_parameters.getResourcesToExportWithMetaData()) { 1395 if (checkPath.startsWith(p)) { 1396 return false; 1397 } 1398 } 1399 return true; 1400 } 1401 1402 /** 1403 * Returns the OpenCms context object this export was initialized with.<p> 1404 * 1405 * @return the OpenCms context object this export was initialized with 1406 */ 1407 protected CmsObject getCms() { 1408 1409 return m_cms; 1410 } 1411 1412 /** 1413 * Returns the name of the export file.<p> 1414 * 1415 * @return the name of the export file 1416 */ 1417 protected String getExportFileName() { 1418 1419 return m_parameters.getPath(); 1420 } 1421 1422 /** 1423 * Returns the name of the main export node.<p> 1424 * 1425 * @return the name of the main export node 1426 */ 1427 protected String getExportNodeName() { 1428 1429 return CmsImportExportManager.N_EXPORT; 1430 } 1431 1432 /** 1433 * Returns the report to write progress messages to.<p> 1434 * 1435 * @return the report to write progress messages to 1436 */ 1437 protected I_CmsReport getReport() { 1438 1439 return m_report; 1440 } 1441 1442 /** 1443 * Returns the name for the main resource node.<p> 1444 * 1445 * @return the name for the main resource node 1446 */ 1447 protected String getResourceNodeName() { 1448 1449 return "files"; 1450 } 1451 1452 /** 1453 * Returns the SAX based xml writer to write the XML output to.<p> 1454 * 1455 * @return the SAX based xml writer to write the XML output to 1456 */ 1457 protected SAXWriter getSaxWriter() { 1458 1459 return m_saxWriter; 1460 } 1461 1462 /** 1463 * Checks if a property should be written to the export or not.<p> 1464 * 1465 * @param property the property to check 1466 * 1467 * @return if true, the property is to be ignored, otherwise it should be exported 1468 */ 1469 protected boolean isIgnoredProperty(CmsProperty property) { 1470 1471 if (property == null) { 1472 return true; 1473 } 1474 if (property.getName().equals(CmsPropertyDefinition.PROPERTY_EXPORT_TYPE)) { 1475 return true; 1476 } 1477 return false; 1478 } 1479 1480 /** 1481 * Checks if a resource is belongs to the correct project for exporting.<p> 1482 * 1483 * @param res the resource to check 1484 * 1485 * @return <code>true</code>, if the resource can be exported, false otherwise 1486 */ 1487 protected boolean isInExportableProject(CmsResource res) { 1488 1489 boolean retValue = true; 1490 // the "only modified in current project flag" is checked 1491 if (m_parameters.isInProject()) { 1492 // resource state is new or changed 1493 if ((res.getState() == CmsResource.STATE_CHANGED) || (res.getState() == CmsResource.STATE_NEW)) { 1494 // the resource belongs not to the current project, so it must not be exported 1495 if (!res.getProjectLastModified().equals(getCms().getRequestContext().getCurrentProject().getUuid())) { 1496 retValue = false; 1497 } 1498 } else { 1499 // state is unchanged, so do not export it 1500 retValue = false; 1501 } 1502 } 1503 return retValue; 1504 } 1505 1506 /** 1507 * Opens the export ZIP file and initializes the internal XML document for the manifest.<p> 1508 * @param exportMode the export mode to use. 1509 * 1510 * @return the node in the XML document where all files are appended to 1511 * 1512 * @throws SAXException if something goes wrong processing the manifest.xml 1513 * @throws IOException if something goes wrong while closing the export file 1514 */ 1515 protected Element openExportFile(ExportMode exportMode) throws IOException, SAXException { 1516 1517 // create the export writer 1518 m_exportWriter = new CmsExportHelper( 1519 getExportFileName(), 1520 m_parameters.isExportAsFiles(), 1521 m_parameters.isXmlValidation()); 1522 // initialize the dom4j writer object as member variable 1523 setSaxWriter(m_exportWriter.getSaxWriter()); 1524 1525 // the node in the XML document where the file entries are appended to 1526 String exportNodeName = getExportNodeName(); 1527 // the XML document to write the XMl to 1528 Document doc = DocumentHelper.createDocument(); 1529 // add main export node to XML document 1530 Element exportNode = doc.addElement(exportNodeName); 1531 getSaxWriter().writeOpen(exportNode); 1532 1533 // add the info element. it contains all infos for this export 1534 Element info = exportNode.addElement(CmsImportExportManager.N_INFO); 1535 if (!exportMode.equals(ExportMode.REDUCED)) { 1536 info.addElement(CmsImportExportManager.N_CREATOR).addText( 1537 getCms().getRequestContext().getCurrentUser().getName()); 1538 info.addElement(CmsImportExportManager.N_OC_VERSION).addText(OpenCms.getSystemInfo().getVersionNumber()); 1539 info.addElement(CmsImportExportManager.N_DATE).addText( 1540 CmsDateUtil.getHeaderDate(System.currentTimeMillis())); 1541 } 1542 info.addElement(CmsImportExportManager.N_INFO_PROJECT).addText( 1543 getCms().getRequestContext().getCurrentProject().getName()); 1544 info.addElement(CmsImportExportManager.N_VERSION).addText(CmsImportExportManager.EXPORT_VERSION); 1545 1546 // write the XML 1547 digestElement(exportNode, info); 1548 1549 return exportNode; 1550 } 1551 1552 /** 1553 * Sets the SAX based XML writer to write the XML output to.<p> 1554 * 1555 * @param saxWriter the SAX based XML writer to write the XML output to 1556 */ 1557 protected void setSaxWriter(SAXWriter saxWriter) { 1558 1559 m_saxWriter = saxWriter; 1560 } 1561 1562 /** 1563 * Cuts leading and trailing '/' from the given resource name.<p> 1564 * 1565 * @param resourceName the absolute path of a resource 1566 * 1567 * @return the trimmed resource name 1568 */ 1569 protected String trimResourceName(String resourceName) { 1570 1571 if (resourceName.startsWith("/")) { 1572 resourceName = resourceName.substring(1); 1573 } 1574 if (resourceName.endsWith("/")) { 1575 resourceName = resourceName.substring(0, resourceName.length() - 1); 1576 } 1577 return resourceName; 1578 } 1579 1580 /** Returns the manifest entry for the <code><datelastmodified></code> node of the resource. 1581 * Depending on the export.timestamp property, the time stamp from the VFS (default) or 1582 * special macros are used. 1583 * 1584 * @param resource the resource for which the manifest entry is generated 1585 * @return the time stamp or macro to write as value for <code><datelastmodified></code> 1586 */ 1587 private String getDateLastModifiedForExport(final CmsResource resource) { 1588 1589 TimestampMode timeMode = TimestampMode.VFSTIME; 1590 String typeName = OpenCms.getResourceManager().getResourceType(resource).getTypeName(); 1591 TimestampMode defaultModeForResourceType = OpenCms.getImportExportManager().getDefaultTimestampMode(typeName); 1592 if (null == defaultModeForResourceType) { 1593 try { 1594 CmsProperty exporttimeProp = m_cms.readPropertyObject( 1595 resource, 1596 CmsImportExportManager.PROP_EXPORT_TIMESTAMP, 1597 true); 1598 if (TimestampMode.FILETIME.equals(TimestampMode.getEnum(exporttimeProp.getValue()))) { 1599 timeMode = TimestampMode.FILETIME; 1600 } else if (TimestampMode.IMPORTTIME.equals(TimestampMode.getEnum(exporttimeProp.getValue()))) { 1601 timeMode = TimestampMode.IMPORTTIME; 1602 } 1603 } catch (@SuppressWarnings("unused") CmsException e) { 1604 // Do nothing, use default mode 1605 } 1606 } else { 1607 timeMode = defaultModeForResourceType; 1608 } 1609 switch (timeMode) { 1610 case FILETIME: 1611 return CmsMacroResolver.formatMacro(CmsImportExportManager.TimestampMode.FILETIME.toString()); 1612 case IMPORTTIME: 1613 return CmsMacroResolver.formatMacro(CmsImportExportManager.TimestampMode.IMPORTTIME.toString()); 1614 case VFSTIME: 1615 default: 1616 return CmsDateUtil.getHeaderDate(resource.getDateLastModified()); 1617 } 1618 1619 } 1620}