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