001/* 002 * Copyright 2019 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.webcontentio.archive; 017 018import java.io.IOException; 019import java.nio.file.DirectoryStream; 020import java.nio.file.Files; 021import java.nio.file.Path; 022import java.util.ArrayList; 023import java.util.List; 024import java.util.Optional; 025import java.util.zip.ZipOutputStream; 026 027import javax.jcr.Node; 028import javax.jcr.NodeIterator; 029import javax.jcr.RepositoryException; 030 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.xml.sax.ContentHandler; 034 035import org.ametys.plugins.contentio.archive.DefaultPluginArchiver; 036import org.ametys.plugins.contentio.archive.ImportReport; 037import org.ametys.plugins.contentio.archive.Merger; 038import org.ametys.plugins.contentio.archive.ResourcesArchiverHelper; 039import org.ametys.plugins.contentio.archive.SystemViewHandler; 040import org.ametys.plugins.contentio.archive.ZipEntryHelper; 041import org.ametys.plugins.explorer.resources.ResourceCollection; 042import org.ametys.plugins.repository.AmetysObjectResolver; 043 044/** 045 * {@link SitePluginArchiver} handling the "web-explorer" part, holding all shared resources. 046 */ 047public class WebExplorerArchiver extends DefaultPluginArchiver 048{ 049 private static final String __WEB_EXPLORER_NODE_NAME = "web-explorer"; 050 private static final String __SHARED_RESOURCES_NODE_NAME = "shared-resources"; 051 052 private AmetysObjectResolver _resolver; 053 private ResourcesArchiverHelper _resourcesArchiverHelper; 054 055 @Override 056 public void service(ServiceManager manager) throws ServiceException 057 { 058 super.service(manager); 059 060 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 061 _resourcesArchiverHelper = (ResourcesArchiverHelper) manager.lookup(ResourcesArchiverHelper.ROLE); 062 } 063 064 @Override 065 public void export(String pluginName, Node node, ZipOutputStream zos, String prefix) throws IOException 066 { 067 super.export(pluginName, node, zos, prefix); 068 069 try 070 { 071 if (node.hasNode(__SHARED_RESOURCES_NODE_NAME)) 072 { 073 Node sharedResourcesNode = node.getNode(__SHARED_RESOURCES_NODE_NAME); 074 NodeIterator it = sharedResourcesNode.getNodes(); 075 while (it.hasNext()) 076 { 077 Node resources = it.nextNode(); 078 ResourceCollection rootResources = _resolver.resolve(resources, false); 079 _resourcesArchiverHelper.exportCollection(rootResources, zos, prefix + "/shared-resources/" + resources.getName() + "/"); 080 } 081 } 082 } 083 catch (RepositoryException e) 084 { 085 throw new RuntimeException("Unable to archive plugin " + pluginName, e); 086 } 087 } 088 089 @Override 090 protected ContentHandler getSystemViewHandler(ContentHandler initialHandler) 091 { 092 return new SystemViewHandler(initialHandler, name -> List.of("ametys:contents", "shared-resources").contains(name), __ -> false); 093 } 094 095 @Override 096 public ImportReport partialImport(String pluginName, Node allPluginsNode, Path zipPath, String zipPluginEntryPath, Merger merger) throws IOException 097 { 098 // ALWAYS remove web-explorer node, even if merger.deleteBeforePartialImport() is false because it MUST be re-created with the good UUID with the super method (JCR import) 099 _removeWebExplorerNode(allPluginsNode); 100 ImportReport importPluginReport = super.partialImport(pluginName, allPluginsNode, zipPath, zipPluginEntryPath, merger); 101 102 Node webExplorerNode = _getWebExplorerNode(allPluginsNode); 103 104 ImportReport importResourceReport = new ImportReport(); 105 String zipSharedResourcesEntryPath = zipPluginEntryPath + "/" + "shared-resources/"; 106 if (ZipEntryHelper.zipEntryFolderExists(zipPath, zipSharedResourcesEntryPath)) 107 { 108 importResourceReport = _importSharedResources(webExplorerNode, zipPath, zipSharedResourcesEntryPath, merger); 109 } 110 111 return ImportReport.union(importPluginReport, importResourceReport); 112 } 113 114 private void _removeWebExplorerNode(Node allPluginsNode) throws IOException 115 { 116 try 117 { 118 if (allPluginsNode.hasNode(__WEB_EXPLORER_NODE_NAME)) 119 { 120 allPluginsNode.getNode(__WEB_EXPLORER_NODE_NAME).remove(); 121 allPluginsNode.getSession().save(); 122 } 123 } 124 catch (RepositoryException e) 125 { 126 throw new IOException(e); 127 } 128 } 129 130 private Node _getWebExplorerNode(Node allPluginsNode) throws IOException 131 { 132 try 133 { 134 Node webExplorerNode = allPluginsNode.getNode(__WEB_EXPLORER_NODE_NAME); 135 return webExplorerNode; 136 } 137 catch (RepositoryException e) 138 { 139 throw new IOException("Unexpected state. The '" + __WEB_EXPLORER_NODE_NAME + "' node was not imported.", e); 140 } 141 } 142 143 private ImportReport _importSharedResources(Node webExplorerNode, Path zipPath, String zipSharedResourcesEntryPath, Merger merger) throws IOException 144 { 145 List<ImportReport> reports = new ArrayList<>(); 146 147 Node sharedResourcesNode; 148 try 149 { 150 sharedResourcesNode = webExplorerNode.hasNode(__SHARED_RESOURCES_NODE_NAME) 151 ? webExplorerNode.getNode(__SHARED_RESOURCES_NODE_NAME) 152 : webExplorerNode.addNode(__SHARED_RESOURCES_NODE_NAME, "ametys:unstructured"); 153 } 154 catch (RepositoryException e) 155 { 156 throw new IOException(e); 157 } 158 159 DirectoryStream<Path> roots = ZipEntryHelper.children( 160 zipPath, 161 Optional.of(zipSharedResourcesEntryPath), 162 Files::isDirectory); 163 try (roots) 164 { 165 for (Path rootPath : roots) 166 { 167 Node parentOfRootResources = sharedResourcesNode; 168 ImportReport importCollectionReport = _resourcesArchiverHelper.importCollection(rootPath.toString() + "/", parentOfRootResources, zipPath, merger); 169 reports.add(importCollectionReport); 170 } 171 } 172 173 return ImportReport.union(reports); 174 } 175}