Index: ivy.xml
===================================================================
--- ivy.xml (revision 19862)
+++ ivy.xml (working copy)
@@ -45,7 +45,6 @@
-
Index: main/plugin-web/i18n/messages_fr.xml
===================================================================
--- main/plugin-web/i18n/messages_fr.xml (revision 19862)
+++ main/plugin-web/i18n/messages_fr.xml (working copy)
@@ -326,6 +326,8 @@
Statistiques du site
Statistiques globales
Supprimer
+ Importer un site
+ Exporter un site
Ouvrir dans le CMS
Nouveau site
Configurer ...
@@ -449,6 +451,29 @@
Explorateur de ressources
Pages
+ Importer un site...
+ Sélectionnez le fichier compressé au format ZIP d'un site Ametys à importer :
+ Fichier ZIP
+ Veuillez sélectionner le fichier zip à charger
+ Parcourir
+ Fichier de type incorrect
+ Vous devez choisir un site Ametys au format ZIP
+ Ok
+ Annuler
+ Téléchargement...
+ Le fichier est en train d'être envoyé sur le serveur. Veuillez patienter.
+ Fichier trop grand
+ Le fichier chargé est trop grand. La taille maximale autorisée se règle dans l'espace 'Configuration'
+ Site existant
+ Un site du même nom que celui envoyé sur le serveur existe déjà.
+ Erreur inconnue
+ Une erreur inconnue a empêché l'import du site
+ Import du site en cours. Veuillez patienter...
+ Import du site
+ L'import du site est terminé.
+ Erreur lors de l'import du site
+ Une erreur est survenue lors de l'import du site. Le site n'a pas pu être correctement importé.
+
Index: main/plugin-web/resources/js/org/ametys/web/administration/Sites.i18n.js
===================================================================
--- main/plugin-web/resources/js/org/ametys/web/administration/Sites.i18n.js (revision 19862)
+++ main/plugin-web/resources/js/org/ametys/web/administration/Sites.i18n.js (working copy)
@@ -42,6 +42,8 @@
this._handle.addAction("", getPluginResourcesUrl(this.pluginName) + "/img/administrator/sites/clear_cache.png", org.ametys.web.administration.site.ClearCache);// 8
this._handle.addAction("", getPluginResourcesUrl(this.pluginName) + "/img/administrator/sites/clear_cache_all.png", org.ametys.web.administration.site.ClearCacheAll);// 9
this._handle.addAction("", getPluginResourcesUrl(this.pluginName) + "/img/administrator/sites/delete.png", org.ametys.web.administration.site.Delete);// 10
+ this._handle.addAction("", getPluginResourcesUrl(this.pluginName) + "/img/administrator/sites/import.png", org.ametys.web.administration.site.Import);// 11
+ this._handle.addAction("", getPluginResourcesUrl(this.pluginName) + "/img/administrator/sites/export.png", org.ametys.web.administration.site.Export);// 12
// Quit action
this._handle.addAction("", getPluginResourcesUrl('core') + '/img/administrator/config/quit.png', org.ametys.web.administration.site.goBack);// 9
@@ -56,6 +58,7 @@
this._handle.hideElt(6);
this._handle.hideElt(8);
this._handle.hideElt(10);
+ this._handle.hideElt(12);
this._rightPanel = new org.ametys.HtmlContainer({
region:'east',
@@ -154,6 +157,7 @@
this._handle.hideElt(6);
this._handle.hideElt(8);
this._handle.hideElt(10);
+ this._handle.hideElt(12);
}
else
{
@@ -164,6 +168,7 @@
this._handle.showElt(6);
this._handle.showElt(8);
this._handle.showElt(10);
+ this._handle.showElt(12);
}
}
@@ -309,6 +314,22 @@
parentNode.select();
}
}
+
+/**-----------------------------------------------------------*/
+
+org.ametys.web.administration.site.Export = function ()
+{
+ var node = org.ametys.web.administration.Site._sites.getSelectionModel().getSelectedNode();
+ var url = getPluginDirectUrl('web') + "/sites/export-site/" + node.attributes.name + ".zip?id=" + node.id;
+ window.open(url);
+}
+
+/**-----------------------------------------------------------*/
+org.ametys.web.administration.site.Import = function ()
+{
+ org.ametys.web.administration.site.Import.act();
+}
+
/**-----------------------------------------------------------*/
org.ametys.web.administration.site.BuildPreview = function()
{
@@ -925,3 +946,210 @@
org.ametys.web.administration.Site._dv.getStore().load();
org.ametys.web.administration.Site._tree.getRootNode().reload();
}
+
+//-------------------------------------------------------
+// SITE IMPORT
+//-------------------------------------------------------
+org.ametys.web.administration.site.Import._initialized = false;
+
+org.ametys.web.administration.site.Import.act = function ()
+{
+ if (!org.ametys.web.administration.site.Import.delayedInitialize())
+ return;
+
+ var node = org.ametys.web.administration.Site._sites.getSelectionModel().getSelectedNode();
+ org.ametys.web.administration.site.Import._parentId = node && node.id || undefined;
+ org.ametys.web.administration.site.Import.box.show();
+ org.ametys.web.administration.site.Import._form.getForm().findField('importfile').reset();
+}
+
+org.ametys.web.administration.site.Import.delayedInitialize = function ()
+{
+ if (org.ametys.web.administration.site.Import._initialized)
+ return true;
+
+ org.ametys.web.administration.site.Import._form = new Ext.form.FormPanel({
+ id : 'import-site',
+ border :false,
+ style: {
+ paddingTop: '8px'
+ },
+
+ fileUpload: true,
+ labelWidth :80,
+ defaultType :'textfield',
+
+ items:[
+ new org.ametys.form.FileUploadField({
+ emptyText: "",
+ fieldLabel :"",
+ buttonText: "",
+ name :'importfile',
+ width :280,
+ listeners: {'fileselected': org.ametys.web.administration.site.Import._select},
+ msgTarget: 'side',
+ })
+ ]
+ });
+
+ org.ametys.web.administration.site.Import.box = new org.ametys.DialogBox({
+
+ title :"",
+ icon : getPluginResourcesUrl("web") + "/img/administrator/sites/import_site_16.png",
+
+ cls: 'text-dialog',
+ bodyStyle: {
+ backgroundColor: '#FFFFFF'
+ },
+ width:450,
+ height:150,
+
+ items : [ new org.ametys.HtmlContainer ({cls: 'dialog-text-hint', html: ""}),
+ org.ametys.web.administration.site.Import._form ],
+
+ defaultButton: org.ametys.web.administration.site.Import._form.getForm().findField('importfile'),
+ closeAction: 'hide',
+ buttons : [ {
+ text :"",
+ disabled: true,
+ handler : org.ametys.web.administration.site.Import.ok
+ }, {
+ text :"",
+ handler : org.ametys.web.administration.site.Import.cancel
+ }
+ ]
+ });
+
+ org.ametys.web.administration.site.Import._initialized = true;
+ return true;
+}
+
+org.ametys.web.administration.site.Import.cancel = function()
+{
+ org.ametys.web.administration.site.Import.box.hide();
+}
+
+
+org.ametys.web.administration.site.Import._filter = function(filename)
+{
+ return /\.zip$/i.test(filename);
+}
+
+org.ametys.web.administration.site.Import._select = function(fileField, name)
+{
+ var disabled = !org.ametys.web.administration.site.Import._filter(name);
+ org.ametys.web.administration.site.Import.box.buttons[0].setDisabled(disabled);
+ if (disabled)
+ {
+ new org.ametys.msg.ErrorDialog("",
+ "",
+ name,
+ "org.ametys.web.administration.site.Import._select");
+
+ fileField.reset();
+ }
+}
+
+org.ametys.web.administration.site.Import.ok = function()
+{
+ org.ametys.web.administration.site.Import.box.hide();
+ org.ametys.web.administration.site.Import._upload();
+}
+
+org.ametys.web.administration.site.Import._upload = function ()
+{
+ org.ametys.web.administration.site.Import._form.getForm().submit({
+ url : getPluginDirectUrl('web') + "/sites/import-site/upload",
+
+ waitTitle: "",
+ waitMsg: "",
+
+ success: org.ametys.web.administration.site.Import._submitSuccess,
+ failure: org.ametys.web.administration.site.Import._submitFailure
+ });
+}
+
+org.ametys.web.administration.site.Import._submitSuccess = function(form, action)
+{
+ org.ametys.web.administration.site.Import._doImport(action.result.siteName, action.result.tmpFilePath);
+}
+
+org.ametys.web.administration.site.Import._doImport = function(siteName, tmpFilePath)
+{
+ org.ametys.web.administration.site.Import._mask = org.ametys.msg.Mask(null, ""); // FIXME msg / el
+
+ var params = {'siteName': siteName, 'tmpFilePath': tmpFilePath, 'deleteFile': 'true', 'parentId' : org.ametys.web.administration.site.Import._parentId};
+ var serverMessage = new org.ametys.servercomm.ServerMessage('web', '/sites/import-site', params, org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, org.ametys.web.administration.site.Import._doImportCallback, this, params, "xml");
+ var response = org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+}
+
+org.ametys.web.administration.site.Import._doImportCallback = function(response, args)
+{
+ org.ametys.web.administration.site.Import._mask.hide();
+
+ if (org.ametys.servercomm.ServerComm.handleBadResponse("", response, 'org.ametys.web.administration.site.Import._doImport'))
+ {
+ return;
+ }
+
+ var success = response.selectSingleNode('ActionResult/success')[org.ametys.servercomm.ServerComm.xmlTextContent];
+
+ if (success === "true")
+ {
+ Ext.Msg.show({
+ title: " (" + args.siteName + ")",
+ msg: "",
+ buttons: Ext.Msg.OK,
+ icon: Ext.Msg.INFO
+ });
+
+ // Reload widget and select the newly imported site
+ var node = org.ametys.web.administration.Site._sites.getNodeById(args.parentId) || org.ametys.web.administration.Site._sites.getRootNode().childNodes[0];
+ if (node)
+ {
+ node.reload(function() {
+ var siteNode = node.findChild('name', args.siteName);
+ if (siteNode)
+ {
+ siteNode.select();
+ console.log(siteNode.attributes.text)
+ }
+ });
+
+ }
+ }
+ else
+ {
+ var error = response.selectSingleNode('ActionResult/error')[org.ametys.servercomm.ServerComm.xmlTextContent];
+ new org.ametys.msg.ErrorDialog("",
+ "",
+ error,
+ "org.ametys.web.administration.site.Import._doImport");
+ }
+
+}
+
+org.ametys.web.administration.site.Import._submitFailure = function(form, action)
+{
+ if (action.result.error == "rejected")
+ {
+ new org.ametys.msg.ErrorDialog("",
+ "",
+ "",
+ "org.ametys.web.administration.site.Import._submitFailure");
+ }
+ if (action.result.error == "site-existing")
+ {
+ new org.ametys.msg.ErrorDialog("",
+ "",
+ "",
+ "org.ametys.web.administration.site.Import._submitFailure");
+ }
+ else
+ {
+ new org.ametys.msg.ErrorDialog("",
+ "",
+ action.result.error.message,
+ "org.ametys.web.administration.site.Import._submitFailure");
+ }
+}
Index: main/plugin-web/plugin.xml
===================================================================
--- main/plugin-web/plugin.xml (revision 19862)
+++ main/plugin-web/plugin.xml (working copy)
@@ -8580,4 +8580,13 @@
+
+
+
+
+
+
+
Index: main/plugin-web/src/org/ametys/web/site/io/importers/SiteNodeImporter.java
===================================================================
--- main/plugin-web/src/org/ametys/web/site/io/importers/SiteNodeImporter.java (revision 0)
+++ main/plugin-web/src/org/ametys/web/site/io/importers/SiteNodeImporter.java (revision 0)
@@ -0,0 +1,177 @@
+package org.ametys.web.site.io.importers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.xml.sax.SAXException;
+
+import org.ametys.cms.io.handlers.ImportAmetysObjectHandler;
+import org.ametys.cms.io.importers.ImportReferenceTracker;
+import org.ametys.cms.io.importers.JcrImporter;
+import org.ametys.cms.repository.CloneComponent;
+import org.ametys.plugins.repository.AmetysObject;
+import org.ametys.plugins.repository.AmetysObjectResolver;
+import org.ametys.plugins.repository.AmetysRepositoryException;
+import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
+import org.ametys.plugins.repository.UnknownAmetysObjectException;
+import org.ametys.web.repository.site.Site;
+import org.ametys.web.repository.site.SiteManager;
+
+/**
+ * Site node importer using the JCR Import.
+ */
+public class SiteNodeImporter extends JcrImporter
+{
+ /** Avalon role. */
+ public static final String ROLE = SiteNodeImporter.class.getName();
+
+ /** Site node import input stream property */
+ public static final String INPUT_STREAM_PROPERTY = "inputStream";
+
+ /** The Ametys object resolver */
+ protected AmetysObjectResolver _resolver;
+
+ /** The site manager */
+ protected SiteManager _siteManager;
+
+ /** The clone component. */
+ protected CloneComponent _cloneComponent;
+
+ @Override
+ public void service(ServiceManager manager) throws ServiceException
+ {
+ super.service(manager);
+ _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
+ _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE);
+ _cloneComponent = (CloneComponent) manager.lookup(CloneComponent.ROLE);
+ }
+
+ /**
+ * Import a site node from an inputstream.
+ * @param parentSiteId
+ * @param siteName
+ * @param importSession The session in which the import will be done.
+ * @param defaultSession
+ * @param inputStream
+ * @param workflowIdMapping
+ * @param referenceTracker
+ * @return the importWorkflowHandler
+ * @throws RepositoryException
+ * @throws SAXException
+ * @throws IOException
+ */
+ public ImportAmetysObjectHandler doImport(String parentSiteId, String siteName, Session importSession, Session defaultSession, InputStream inputStream, Map workflowIdMapping, ImportReferenceTracker referenceTracker) throws RepositoryException, SAXException, IOException
+ {
+ Node siteParentNode = _getSiteParentNode(parentSiteId, siteName, importSession, defaultSession);
+
+ // Get the import handler
+ int uuidBehavior = ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW; // An exception will be thrown if there is an identifier collision.
+ ImportAmetysObjectHandler importHandlerProxy = new ImportAmetysObjectHandler(siteParentNode, referenceTracker, workflowIdMapping);
+
+ // Performing the import.
+ importNode(siteParentNode, uuidBehavior, inputStream, importHandlerProxy);
+
+ return importHandlerProxy;
+ }
+
+ /**
+ * Get (and create if necessary) the parent node of a site in the JCR hierarchy.
+ * @param parentSiteId the ametys id of the parent site. Null if the site has no parent.
+ * @param siteName Name of the site to import.
+ * @param importSession The session in which the import is done
+ * @param defaultSession Session bound to the default workspace.
+ * @return The parent node of the site node.
+ * @throws RepositoryException
+ */
+ private Node _getSiteParentNode(String parentSiteId, String siteName, Session importSession, Session defaultSession) throws RepositoryException
+ {
+ if (importSession.getWorkspace().getName().equals(defaultSession.getWorkspace().getName()))
+ {
+ // Default workspace.
+ Site parentSite = _getSite(parentSiteId, importSession);
+ return _getSiteParentNode(parentSite, siteName, importSession);
+ }
+ else
+ {
+ // Other workspaces (should be 'archives'):
+ // Import must be done is the default workspace first. So the site
+ // must exist in the default workspace while importing into another
+ // workspace.
+ if (_siteManager.hasSite(siteName))
+ {
+ Node siteNodeDefaultWS = _siteManager.getSite(siteName).getNode();
+ return _cloneComponent.cloneAncestorsAndPreserveUUID(siteNodeDefaultWS, importSession);
+ }
+ }
+
+ String errorMsg = "Unable to get the parent node of the site node to import.";
+ getLogger().error(errorMsg);
+ throw new IllegalStateException(errorMsg);
+ }
+
+ /**
+ * Get (and create if necessary) the parent node of a site in the JCR hierarchy.
+ * @param parentSite Parent site of the site to import.
+ * @param siteName Name of the site to import.
+ * @param session The session used to retrieve and create nodes if the parent site is null.
+ * @return
+ * @throws RepositoryException
+ */
+ private Node _getSiteParentNode(Site parentSite, String siteName, Session session) throws RepositoryException
+ {
+ ModifiableTraversableAmetysObject parentRootSites = null;
+
+ if (parentSite == null)
+ {
+ // Retrieves the root Ametys object where "ametys:site" are created within the jcrSession
+ // We currently use a workaround, see REPOSITORY-212
+ // Must do _resolver.resolve(SiteManager.ROOT_SITES_PATH, jcrSession); when the API will allow it.
+ Node rootSitesNode = session.getRootNode().getNode(AmetysObjectResolver.ROOT_REPO + SiteManager.ROOT_SITES_PATH);
+ parentRootSites = _resolver.resolve(rootSitesNode, false);
+ }
+ else
+ {
+ // Retrieves the Ametys object where sub-sites are created for parentSite.
+ if (!parentSite.hasChild(SiteManager.ROOT_SITES))
+ {
+ parentSite.createChild(SiteManager.ROOT_SITES, "ametys:sites");
+ }
+ parentRootSites = parentSite.getChild(SiteManager.ROOT_SITES);
+ }
+
+ // Create the child site, keep a reference to its direct parent node and
+ // remove the node of the site.
+ Node siteNode = ((Site) parentRootSites.createChild(siteName, "ametys:site")).getNode();
+ Node parentNode = siteNode.getParent();
+ siteNode.remove();
+
+ return parentNode;
+ }
+
+ /**
+ * Retrieves a {@link Site} by its id.
+ * @param jcrSession
+ * @throws RepositoryException
+ * @throws AmetysRepositoryException
+ * @throws UnknownAmetysObjectException
+ * @Return the {@link Site} instance, or null if the retrieved {@link AmetysObject} is not a {@link Site}
+ */
+ private Site _getSite(String id, Session jcrSession) throws RepositoryException
+ {
+ if (id == null)
+ {
+ return null;
+ }
+
+ AmetysObject ao = _resolver.resolveById(id, jcrSession);
+ return ao instanceof Site ? (Site) ao : null;
+ }
+}
Index: main/plugin-web/src/org/ametys/web/site/io/importers/SiteImporter.java
===================================================================
--- main/plugin-web/src/org/ametys/web/site/io/importers/SiteImporter.java (revision 0)
+++ main/plugin-web/src/org/ametys/web/site/io/importers/SiteImporter.java (revision 0)
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2012 Anyware Services
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ametys.web.site.io.importers;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Workspace;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionHistory;
+
+import org.apache.avalon.framework.component.Component;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.commons.lang.BooleanUtils;
+import org.xml.sax.SAXException;
+
+import org.ametys.cms.content.archive.ArchiveConstants;
+import org.ametys.cms.io.IOConstants;
+import org.ametys.cms.io.IOUtils;
+import org.ametys.cms.io.handlers.ImportAmetysObjectHandler;
+import org.ametys.cms.io.handlers.ImportWorkflowHandler;
+import org.ametys.cms.io.importers.AmetysImporter;
+import org.ametys.cms.io.importers.BinaryPropertiesImporter;
+import org.ametys.cms.io.importers.ImportReferenceTracker;
+import org.ametys.cms.io.importers.ResourcesImporter;
+import org.ametys.cms.io.importers.ResourcesImporter.BinaryResourceImportData;
+import org.ametys.cms.io.importers.ResourcesImporter.ResourceNodeImportData;
+import org.ametys.cms.io.importers.WorkflowImporter;
+import org.ametys.plugins.repository.AmetysObjectResolver;
+import org.ametys.plugins.repository.provider.AbstractRepository;
+import org.ametys.plugins.workflow.Workflow;
+import org.ametys.plugins.workflow.store.AmetysStep;
+import org.ametys.web.WebConstants;
+
+import com.opensymphony.workflow.spi.Step;
+
+/**
+ * Site importer
+ */
+public class SiteImporter extends AmetysImporter implements Component, Serviceable
+{
+ /** Avalon role. */
+ public static final String ROLE = SiteImporter.class.getName();
+
+ // Key used to configure the importers
+ /** configuration key of the site node importer */
+ public static final String SITE_NODE_PROPERTIES = "siteNode";
+ /** configuration key of the workflow importer */
+ public static final String WORKFLOWS_PROPERTIES = "workflows";
+ /** configuration key of the resource importer */
+ public static final String RESOURCES_PROPERTIES = "resources";
+ /** configuration key of the binary properties importer */
+ public static final String BINARY_PROPERTIES = "binary";
+
+ /** JCR Repository */
+ protected Repository _repository;
+
+ /** The workflow */
+ protected Workflow _workflow;
+
+ /** The workflow importer */
+ protected WorkflowImporter _workflowImporter;
+
+ /** The site node importer */
+ protected SiteNodeImporter _siteNodeImporter;
+
+ /** The binary properties importer */
+ protected BinaryPropertiesImporter _binaryPropertiesImporter;
+
+ /** The ametys resources exporter */
+ protected ResourcesImporter _resourcesImporter;
+
+ /** Map containing the properties for each exporter used during the global import */
+ protected Map _importerProperties = new HashMap();
+
+ @Override
+ public void service(ServiceManager manager) throws ServiceException
+ {
+ _repository = (Repository) manager.lookup(AbstractRepository.ROLE);
+ _workflowImporter = (WorkflowImporter) manager.lookup(WorkflowImporter.ROLE);
+ _siteNodeImporter = (SiteNodeImporter) manager.lookup(SiteNodeImporter.ROLE);
+ _binaryPropertiesImporter = (BinaryPropertiesImporter) manager.lookup(BinaryPropertiesImporter.ROLE);
+ _resourcesImporter = (ResourcesImporter) manager.lookup(ResourcesImporter.ROLE);
+ _workflow = (Workflow) manager.lookup(Workflow.ROLE);
+ }
+
+
+ /**
+ * Proceed the import of a site into the default workspace.
+ * @param parentSiteId
+ * @param siteName
+ * @param importerProperties
+ * @return The imported node
+ * @throws RepositoryException
+ * @throws SAXException
+ * @throws IOException
+ */
+ public Node doImport(String parentSiteId, String siteName, Map importerProperties) throws RepositoryException, SAXException, IOException
+ {
+ Session session = null;
+
+ try
+ {
+ session = _repository.login();
+ return _doImportInternal(parentSiteId, siteName, importerProperties, session, session);
+ }
+ finally
+ {
+ if (session != null)
+ {
+ session.logout();
+ }
+ }
+ }
+
+ /**
+ * Proceed the import of a site into the archive workspace.
+ * @param parentSiteId
+ * @param siteName
+ * @param importerProperties
+ * @return The imported node
+ * @throws RepositoryException
+ * @throws SAXException
+ * @throws IOException
+ */
+ public Node doImportArchives(String parentSiteId, String siteName, Map importerProperties) throws RepositoryException, SAXException, IOException
+ {
+ Session defaultSession = null;
+ Session archiveSession = null;
+
+ try
+ {
+ archiveSession = _getArchiveSession();
+ defaultSession = _repository.login();
+ return _doImportInternal(parentSiteId, siteName, importerProperties, archiveSession, defaultSession);
+ }
+ finally
+ {
+ if (archiveSession != null)
+ {
+ archiveSession.logout();
+ }
+
+ if (defaultSession != null)
+ {
+ defaultSession.logout();
+ }
+ }
+ }
+
+
+ /**
+ * Internal method where the import is actually done.
+ * @param parentSiteId
+ * @param siteName
+ * @param importerProperties
+ * @param importSession
+ * @param defaultSession
+ * @return The imported node
+ * @throws RepositoryException
+ * @throws SAXException
+ * @throws IOException
+ */
+ protected Node _doImportInternal(String parentSiteId, String siteName, Map importerProperties, Session importSession, Session defaultSession) throws RepositoryException, SAXException, IOException
+ {
+ ImportReferenceTracker referenceTracker = new ImportReferenceTracker();
+
+ // import workflow
+ ImportWorkflowHandler importWorkflowHandler = _importWorkflow(importSession, defaultSession, importerProperties.get(WORKFLOWS_PROPERTIES), referenceTracker);
+
+ // import site node
+ ImportAmetysObjectHandler importSiteHandler = _importSiteNode(parentSiteId, siteName, importSession, defaultSession, importerProperties.get(SITE_NODE_PROPERTIES), importWorkflowHandler.getIdMapping(), referenceTracker);
+ Node siteNode = importSiteHandler.getImportedNode();
+
+ // adjust references
+ _adjustReferenceProperties(siteNode, referenceTracker);
+
+ // workflows and site node are now imported, a save is possible.
+ importSession.save();
+ defaultSession.save();
+
+ // import resources
+ _importResources(siteNode, importerProperties.get(RESOURCES_PROPERTIES));
+
+ // import binary properties
+ _importBinaryProperties(importSession, importerProperties.get(BINARY_PROPERTIES));
+
+ // adjust versionable nodes
+ _adjustVersionableNodes(siteNode, importSiteHandler.getExportDate());
+
+ // final save
+ importSession.save();
+
+ return siteNode;
+ }
+
+ /**
+ * Import the workflow from the given input stream.
+ * @param importSession
+ * @param defaultSession
+ * @param properties
+ * @param referenceTracker
+ * @return ImportWorkflowHandler
+ * @throws RepositoryException
+ * @throws SAXException
+ * @throws IOException
+ */
+ protected ImportWorkflowHandler _importWorkflow(Session importSession, Session defaultSession, Properties properties, ImportReferenceTracker referenceTracker) throws RepositoryException, SAXException, IOException
+ {
+ InputStream inputStream = (InputStream) properties.get(WorkflowImporter.INPUT_STREAM_PROPERTY);
+ return _workflowImporter.doImport(importSession, defaultSession, inputStream, referenceTracker);
+ }
+
+ /**
+ * Import the site node from a given input stream.
+ * @param parentSiteId
+ * @param siteName
+ * @param importSession
+ * @param defaultSession
+ * @param properties
+ * @param workflowIdMapping
+ * @param referenceTracker
+ * @return ImportAmetysObjectHandler
+ * @throws RepositoryException
+ * @throws SAXException
+ * @throws IOException
+ */
+ protected ImportAmetysObjectHandler _importSiteNode(String parentSiteId, String siteName, Session importSession, Session defaultSession, Properties properties, Map workflowIdMapping, ImportReferenceTracker referenceTracker) throws RepositoryException, SAXException, IOException
+ {
+ InputStream inputStream = (InputStream) properties.get(SiteNodeImporter.INPUT_STREAM_PROPERTY);
+ return _siteNodeImporter.doImport(parentSiteId, siteName, importSession, defaultSession, inputStream, workflowIdMapping, referenceTracker);
+ }
+
+ /**
+ * Import the binary properties
+ * @param importSession
+ * @param properties
+ * @throws RepositoryException
+ */
+ protected void _importBinaryProperties(Session importSession, Properties properties) throws RepositoryException
+ {
+ List importDataList = (List) properties.get(BinaryPropertiesImporter.IMPORT_DATA_LIST_PROPERTY);
+ _binaryPropertiesImporter.doImport(importSession, importDataList);
+ }
+
+ /**
+ * Import the ametys resources
+ * @param siteNode
+ * @param properties
+ * @throws IOException
+ * @throws SAXException
+ * @throws RepositoryException
+ */
+ protected void _importResources(Node siteNode, Properties properties) throws RepositoryException, SAXException, IOException
+ {
+ List nodeDataList = (List) properties.get(ResourcesImporter.IMPORT_RESOURCE_NODE_LIST_PROPERTY);
+ List binaryDataList = (List) properties.get(ResourcesImporter.IMPORT_BINARY_RESOURCE_LIST_PROPERTY);
+ _resourcesImporter.doImport(siteNode, nodeDataList, binaryDataList);
+ }
+
+ /**
+ * Retrieve a session bound to the 'archives' workspace. Create and
+ * initialize the 'archives' workspace if necessary.
+ *
+ * @return
+ * @throws RepositoryException
+ */
+ private Session _getArchiveSession() throws RepositoryException
+ {
+ try
+ {
+ return _repository.login(ArchiveConstants.ARCHIVE_WORKSPACE);
+ }
+ catch (NoSuchWorkspaceException e)
+ {
+ _repository.login().getWorkspace().createWorkspace(ArchiveConstants.ARCHIVE_WORKSPACE);
+
+ Session archiveSession = _repository.login(ArchiveConstants.ARCHIVE_WORKSPACE);
+ archiveSession.getRootNode().addNode(AmetysObjectResolver.ROOT_REPO, AmetysObjectResolver.ROOT_TYPE);
+ return archiveSession;
+ }
+ }
+
+ @Override
+ protected void _additionalAdjustementForVersionableNode(Node node, VersionHistory versionHistory) throws RepositoryException
+ {
+ Workspace importWorkspace = node.getSession().getWorkspace();
+ boolean isArchiveWS = ArchiveConstants.ARCHIVE_WORKSPACE.equals(importWorkspace.getName());
+
+ // Add live label on validated version.
+ if (!isArchiveWS)
+ {
+ _addLiveLabelIfNecessary(node, versionHistory);
+ }
+ }
+
+ /**
+ * Add the live label on the current version if necessary
+ * @param node
+ * @param versionHistory
+ * @throws RepositoryException
+ */
+ protected void _addLiveLabelIfNecessary(Node node, VersionHistory versionHistory) throws RepositoryException
+ {
+ if (node.isNodeType(IOConstants.DEFAULT_CONTENT_NODETYPE) && node.hasProperty(IOConstants.WORKFLOW_ID_PROPERTY))
+ {
+ List currentSteps = _workflow.getCurrentSteps(node.getProperty(IOConstants.WORKFLOW_ID_PROPERTY).getLong());
+ Step currentStep = currentSteps.iterator().next();
+
+ if (currentStep instanceof AmetysStep)
+ {
+ Boolean validation = (Boolean) ((AmetysStep) currentStep).getProperty("validation");
+ if (BooleanUtils.isTrue(validation))
+ {
+ Iterator versionsIterator = IOUtils.getVersionSortedByCreatedDesc(versionHistory);
+ Version currentVersion = versionsIterator.next();
+ versionHistory.addVersionLabel(currentVersion.getName(), WebConstants.LIVE_LABEL, true);
+ }
+ }
+ }
+ }
+}
Index: main/plugin-web/src/org/ametys/web/site/io/ImportSiteAction.java
===================================================================
--- main/plugin-web/src/org/ametys/web/site/io/ImportSiteAction.java (revision 0)
+++ main/plugin-web/src/org/ametys/web/site/io/ImportSiteAction.java (revision 0)
@@ -0,0 +1,423 @@
+/*
+ * Copyright 2012 Anyware Services
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ametys.web.site.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.acting.ServiceableAction;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipFile;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.StringUtils;
+import org.xml.sax.SAXException;
+
+import org.ametys.cms.io.IOConstants;
+import org.ametys.cms.io.IOUtils;
+import org.ametys.cms.io.importers.BinaryPropertiesImporter;
+import org.ametys.cms.io.importers.ResourcesImporter;
+import org.ametys.cms.io.importers.ResourcesImporter.BinaryResourceImportData;
+import org.ametys.cms.io.importers.WorkflowImporter;
+import org.ametys.web.live.RebuildLiveComponent;
+import org.ametys.web.repository.content.jcr.DefaultWebContent;
+import org.ametys.web.repository.site.Site;
+import org.ametys.web.repository.site.SiteManager;
+import org.ametys.web.site.io.importers.SiteImporter;
+import org.ametys.web.site.io.importers.SiteNodeImporter;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+
+/**
+ * This action import a site from a file located in "java.io.tmpdir"
+ */
+public class ImportSiteAction extends ServiceableAction
+{
+ /** The site importer */
+ protected SiteImporter _siteImporter;
+
+ /** The site manager */
+ protected SiteManager _siteManager;
+
+ /** The rebuild live component */
+ protected RebuildLiveComponent _rebuildLiveComponent;
+
+ @Override
+ public void service(ServiceManager smanager) throws ServiceException
+ {
+ super.service(smanager);
+ _siteImporter = (SiteImporter) smanager.lookup(SiteImporter.ROLE);
+ _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE);
+ _rebuildLiveComponent = (RebuildLiveComponent) smanager.lookup(RebuildLiveComponent.ROLE);
+ }
+
+ @Override
+ public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
+ {
+ Map result = new LinkedHashMap();
+ Request request = ObjectModelHelper.getRequest(objectModel);
+
+ String siteName = request.getParameter("siteName");
+ String tmpFilePath = request.getParameter("tmpFilePath");
+ boolean deleteFile = new Boolean(request.getParameter("deleteFile"));
+ String parentSiteId = request.getParameter("parentId");
+
+ // Site name must be unique.
+ if (_siteManager.hasSite(siteName))
+ {
+ String msg = "Unable to import site. The site '" + siteName + "' already exists.";
+ getLogger().error(msg);
+ result.put("success", false);
+ result.put("error", msg);
+ return result;
+ }
+
+ File tmpArchiveFile = null;
+ ZipFile siteZipFile = null;
+ boolean rebuildError = false;
+ try
+ {
+ tmpArchiveFile = new File(tmpFilePath);
+ siteZipFile = new ZipFile(tmpArchiveFile, "UTF-8", true);
+
+ Site site = _importSiteFromZip(siteZipFile, parentSiteId, siteName);
+
+ // Rebuild live of the site
+ try
+ {
+ _rebuildLiveComponent.rebuildLive(site);
+ }
+ catch (Exception e)
+ {
+ rebuildError = true;
+ throw e;
+ }
+
+ result.put("success", true);
+ }
+ catch (Exception e)
+ {
+ String msg = "";
+ if (rebuildError)
+ {
+ msg = "The site '" + siteName + "' has been successfully imported but the live rebuilt of the site has failed.\nTry rebuilding the live of a site again.";
+ }
+ else
+ {
+ msg = "Unable to import the site '" + siteName + "'";
+ }
+
+ getLogger().error(msg, e);
+
+ msg += "\nError message : " + e;
+ result.put("success", false);
+ result.put("error", msg);
+ }
+ finally
+ {
+ if (siteZipFile != null)
+ {
+ siteZipFile.close();
+ }
+
+ if (deleteFile)
+ {
+ FileUtils.deleteQuietly(tmpArchiveFile);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Traverse the site archive and import data on the fly.
+ * @param siteZipFile The ZipFile of the archive
+ * @param parentSiteId The parent {@link Site} object.
+ * @param siteName Name of the site to be imported
+ * @return The imported site.
+ * @throws IOException
+ * @throws SAXException
+ * @throws RepositoryException
+ */
+ private Site _importSiteFromZip(ZipFile siteZipFile, String parentSiteId, String siteName) throws IOException, SAXException, RepositoryException
+ {
+ ZipArchiveEntry entry = null;
+
+ // Traverse the site archive to class entry by workspace (default or archives).
+ Set archivesEntries = new HashSet();
+ Set defaultEntries = new HashSet();
+ for (Enumeration entries = siteZipFile.getEntries(); entries.hasMoreElements();)
+ {
+ entry = entries.nextElement();
+ if (!entry.isDirectory())
+ {
+ String entryName = entry.getName();
+ if (entryName.startsWith(IOConstants.ARCHIVES_DIR))
+ {
+ archivesEntries.add(entry);
+ }
+ else
+ {
+ defaultEntries.add(entry);
+ }
+ }
+ }
+
+ // Import site (default + archives workspace)
+ _importSite(siteZipFile, defaultEntries, parentSiteId, siteName);
+ _importArchives(siteZipFile, archivesEntries, parentSiteId, siteName);
+
+ return _siteManager.getSite(siteName);
+ }
+
+ private void _importSite(ZipFile siteZipFile, Set defaultEntries, String parentSiteId, String siteName) throws IOException, RepositoryException, SAXException
+ {
+ _importSite(siteZipFile, defaultEntries, parentSiteId, siteName, false);
+ }
+
+ private void _importArchives(ZipFile siteZipFile, Set archivesEntries, String parentSiteId, String siteName) throws IOException, RepositoryException, SAXException
+ {
+ if (archivesEntries.isEmpty())
+ {
+ return;
+ }
+
+ _importSite(siteZipFile, archivesEntries, parentSiteId, siteName, true);
+ }
+
+ private void _importSite(ZipFile siteZipFile, Set entries, String parentSiteId, String siteName, boolean isArchivesWS) throws IOException, RepositoryException, SAXException
+ {
+ String baseEntryName = isArchivesWS ? IOConstants.ARCHIVES_DIR : StringUtils.EMPTY;
+
+ // Configure site import
+ Map siteImporterProperties = new HashMap();
+ Properties props = new Properties();
+ ZipArchiveEntry siteEntry = siteZipFile.getEntry(baseEntryName + IOSiteConstants.SITE_FILE_NAME);
+ props.put(SiteNodeImporter.INPUT_STREAM_PROPERTY, siteZipFile.getInputStream(siteEntry));
+ siteImporterProperties.put(SiteImporter.SITE_NODE_PROPERTIES, props);
+
+ // Configure workflow import
+ props = new Properties();
+ ZipArchiveEntry workflowEntry = siteZipFile.getEntry(baseEntryName + IOConstants.WORKFLOWS_FILE_NAME);
+ props.put(WorkflowImporter.INPUT_STREAM_PROPERTY, siteZipFile.getInputStream(workflowEntry));
+ siteImporterProperties.put(SiteImporter.WORKFLOWS_PROPERTIES, props);
+
+ // Traverse the zip file class entries by type (data or resources)
+ Set dataEntries = new HashSet();
+ Set resourcesEntries = new HashSet();
+ for (ZipArchiveEntry entry : entries)
+ {
+ String entryName = entry.getName();
+ if (entryName.startsWith(IOConstants.BINARY_DIR))
+ {
+ dataEntries.add(entry);
+ }
+ else if (entryName.startsWith(IOConstants.EXPLORER_DIR) || entryName.startsWith(IOSiteConstants.ATTACHMENTS_DIR))
+ {
+ resourcesEntries.add(entry);
+ }
+ }
+
+ // Configure ametys resources import
+ props = _getResourcesProperties(siteZipFile, resourcesEntries, baseEntryName);
+ siteImporterProperties.put(SiteImporter.RESOURCES_PROPERTIES, props);
+
+ // Configure binary properties import
+ props = _getBinaryDataProperties(siteZipFile, dataEntries);
+ siteImporterProperties.put(SiteImporter.BINARY_PROPERTIES, props);
+
+ // Run the import
+ if (isArchivesWS)
+ {
+ _siteImporter.doImportArchives(parentSiteId, siteName, siteImporterProperties);
+ }
+ else
+ {
+ _siteImporter.doImport(parentSiteId, siteName, siteImporterProperties);
+ }
+ }
+
+ /**
+ * Retrieves the Properties
needed to configure the
+ * BinaryPropertyImporter
used in the site import.
+ *
+ * @param siteZipFile
+ * @param dataEntries Entries representing a binary property to import.
+ * @return A Properties
object to pass the to site importer.
+ * @throws IOException
+ * @see BinaryPropertiesImporter
+ */
+ protected Properties _getBinaryDataProperties(ZipFile siteZipFile, Set dataEntries) throws IOException
+ {
+ List importDataList = new ArrayList();
+
+ // Iterate through all data entries, and populate the import data list.
+ for (ZipArchiveEntry binaryEntry : dataEntries)
+ {
+ String[] splittedEntryName = StringUtils.split(binaryEntry.getName(), '/');
+ int len = splittedEntryName.length;
+ String identifier = splittedEntryName[len - 2];
+ String propertyName = IOUtils.decodeZipEntryName(splittedEntryName[len - 1]);
+
+ importDataList.add(new BinaryPropertiesImporter.ImportData(identifier, propertyName, siteZipFile.getInputStream(binaryEntry)));
+ }
+
+ Properties props = new Properties();
+ props.put(BinaryPropertiesImporter.IMPORT_DATA_LIST_PROPERTY, importDataList);
+
+ return props;
+ }
+
+ /**
+ * Retrieves the Properties
needed to configure the
+ * BinaryPropertyImporter
used in the site import.
+ *
+ * @param siteZipFile
+ * @param resourcesEntries Entries representing an ametys resource to import.
+ * @param baseEntryName Beginning of the name of the resourcesEntries corresponding to a context (e.g "" or "archives/"..)
+ * @return A Properties
object to pass the to site importer.
+ * @throws IOException
+ * @see ResourcesImporter
+ */
+ protected Properties _getResourcesProperties(ZipFile siteZipFile, Set resourcesEntries, String baseEntryName) throws IOException
+ {
+ // Map linking the start of the entry name to the start of the relative path.
+ final Map entryPathMapper = new HashMap();
+ entryPathMapper.put(IOConstants.EXPLORER_DIR, StringUtils.EMPTY);
+ entryPathMapper.put(IOSiteConstants.ATTACHMENTS_DIR + IOSiteConstants.CONTENTS_ATTACHMENTS_DIR, IOConstants.CONTENTS_NODE_NAME + '/');
+ entryPathMapper.put(IOSiteConstants.ATTACHMENTS_DIR + IOSiteConstants.PAGES_ATTACHMENTS_DIR, IOSiteConstants.SITEMAPS_NODE_NAME + '/');
+
+ // Map linking the start of the entry name to the resource node name
+ final Map resourceNodeNameMapper = new HashMap();
+ resourceNodeNameMapper.put(IOConstants.EXPLORER_DIR, IOConstants.RESOURCES_NODE_NAME);
+ resourceNodeNameMapper.put(IOSiteConstants.ATTACHMENTS_DIR + IOSiteConstants.CONTENTS_ATTACHMENTS_DIR, DefaultWebContent.ATTACHMENTS_NODE_NAME);
+ resourceNodeNameMapper.put(IOSiteConstants.ATTACHMENTS_DIR + IOSiteConstants.PAGES_ATTACHMENTS_DIR, DefaultWebContent.ATTACHMENTS_NODE_NAME);
+
+ // Populate the node data list to be imported and a multimap
+ // (mappedEntries) that will be used in the next for-loop.
+ final ListMultimap mappedEntries = ArrayListMultimap.create();
+ List nodeDataList = new ArrayList();
+ for (ZipArchiveEntry resourceEntry : resourcesEntries)
+ {
+ // Ignores directory
+ if (resourceEntry.isDirectory())
+ {
+ continue;
+ }
+
+ // Retrieve the relative path of the resource node given the entry name.
+ String entryName = StringUtils.removeStart(resourceEntry.getName(), baseEntryName);
+ String entryStart = null;
+ String relPath = null;
+ for (String start : entryPathMapper.keySet())
+ {
+ if (entryName.startsWith(start))
+ {
+ entryStart = start;
+ relPath = entryPathMapper.get(start) + StringUtils.removeStart(entryName, start);
+ }
+ }
+
+ if (relPath == null || entryStart == null)
+ {
+ String msg = "Resource entry name is not valid.";
+ getLogger().error(msg);
+ throw new IOException(msg);
+ }
+
+ // Import resource.sv files.
+ if (_isResourceSvFilePath(relPath))
+ {
+ relPath = StringUtils.removeEnd(relPath, IOConstants.RESOURCES_FILE_NAME);
+ relPath = StringUtils.removeEnd(relPath, "/");
+ nodeDataList.add(new ResourcesImporter.ResourceNodeImportData(relPath, siteZipFile.getInputStream(resourceEntry)));
+ }
+ // Populate the mappedEntries multimap.
+ else
+ {
+ // At this stage, path should contains the special "_resources" folder.
+ relPath = relPath.startsWith(IOConstants.RESOURCES_DIR) ? StringUtils.EMPTY : StringUtils.substringBefore(relPath, '/' + IOConstants.RESOURCES_DIR) + '/';
+ relPath += resourceNodeNameMapper.get(entryStart);
+ mappedEntries.put(relPath, resourceEntry);
+ }
+ }
+
+ // Populate the binary resource data list to be imported
+ List binaryDataList = new ArrayList();
+ for (String resourceCollectionPath : mappedEntries.keySet())
+ {
+ for (ZipArchiveEntry entry : mappedEntries.get(resourceCollectionPath))
+ {
+ String entryName = entry.getName();
+ String resourcePath = null;
+ if (entryName.startsWith(IOConstants.RESOURCES_DIR))
+ {
+ resourcePath = StringUtils.removeStart(entryName, IOConstants.RESOURCES_DIR);
+ }
+ else
+ {
+ resourcePath = StringUtils.substringAfter(entryName, '/' + IOConstants.RESOURCES_DIR);
+ }
+ resourcePath = IOUtils.decodeZipEntryName(resourcePath);
+
+ binaryDataList.add(new BinaryResourceImportData(resourceCollectionPath + '/' + resourcePath, siteZipFile.getInputStream(entry)));
+ }
+ }
+
+ Properties props = new Properties();
+ props.put(ResourcesImporter.IMPORT_RESOURCE_NODE_LIST_PROPERTY, nodeDataList);
+ props.put(ResourcesImporter.IMPORT_BINARY_RESOURCE_LIST_PROPERTY, binaryDataList);
+
+ return props;
+ }
+
+ /**
+ * Indicates if a relative path denotes a path to a XML "resource" system
+ * view. The path must end with {@link IOConstants#RESOURCES_FILE_NAME}
+ * and must not contain a folder equals to
+ * {@link IOConstants#RESOURCES_DIR}
+ * @param relPath The relative path to be tested
+ * @return True if relPath match the conditions above.
+ */
+ protected boolean _isResourceSvFilePath(String relPath)
+ {
+ if (relPath.endsWith(IOConstants.RESOURCES_FILE_NAME))
+ {
+ String mustNotContain = StringUtils.removeEnd(IOConstants.RESOURCES_DIR, "/");
+ return !ArrayUtils.contains(StringUtils.split(relPath, '/'), mustNotContain);
+ }
+ return false;
+ }
+}
Index: main/plugin-web/src/org/ametys/web/site/io/UploadSiteAction.java
===================================================================
--- main/plugin-web/src/org/ametys/web/site/io/UploadSiteAction.java (revision 0)
+++ main/plugin-web/src/org/ametys/web/site/io/UploadSiteAction.java (revision 0)
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2012 Anyware Services
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ametys.web.site.io;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.acting.ServiceableAction;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.Request;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.cocoon.servlet.multipart.Part;
+import org.apache.cocoon.servlet.multipart.PartOnDisk;
+import org.apache.cocoon.servlet.multipart.RejectedPart;
+import org.apache.commons.io.FileUtils;
+
+import org.ametys.runtime.cocoon.JSonReader;
+import org.ametys.web.repository.site.SiteManager;
+
+/**
+ * This action receives a form with the "importfile" file.
+ * This file contains a serialized site to be imported.
+ */
+public class UploadSiteAction extends ServiceableAction
+{
+ /** The site manager */
+ protected SiteManager _siteManager;
+
+ @Override
+ public void service(ServiceManager smanager) throws ServiceException
+ {
+ super.service(smanager);
+ _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE);
+ }
+
+ @Override
+ public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
+ {
+ Request request = ObjectModelHelper.getRequest(objectModel);
+ Map result = new LinkedHashMap();
+
+ try
+ {
+ // Checking file
+ Part part = (Part) request.get("importfile");
+ if (part instanceof RejectedPart || part == null)
+ {
+ result.put("success", false);
+ result.put("error", "rejected");
+ }
+ else
+ {
+ // Getting file
+ PartOnDisk uploadedFilePart = (PartOnDisk) part;
+ File uploadedFile = uploadedFilePart.getFile();
+
+ String filename = (String) uploadedFilePart.getHeaders().get("filename");
+ String siteName = filename.substring(0, filename.length() - 4);
+
+ // Site name must be unique.
+ if (!_siteManager.hasSite(siteName))
+ {
+ // Copy the file into the default tmp dir.
+ File tmpFile = _copyToTmpDir(uploadedFile, siteName);
+
+ result.put("tmpFilePath", tmpFile.getPath());
+ result.put("siteName", siteName);
+ result.put("success", true);
+ }
+ else
+ {
+ String msg = "Unable to upload site. The site '" + siteName + "' already exists.";
+ getLogger().error(msg);
+ result.put("error", "site-existing");
+ result.put("success", false);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ getLogger().error("Unable to upload the site", e);
+ result.put("success", false);
+
+ Map ex = new HashMap();
+ ex.put("message", e.getMessage());
+
+ result.put("error", ex);
+ }
+
+ request.setAttribute(JSonReader.MAP_TO_READ, result);
+ return EMPTY_MAP;
+ }
+
+ /**
+ * Copy the uploaded file in a temporary directory.
+ * @param uploadedFile the uploaded file
+ * @param siteName
+ * @return the copied file instance
+ * @throws IOException
+ */
+ protected File _copyToTmpDir(File uploadedFile, String siteName) throws IOException
+ {
+ File destFile = File.createTempFile(siteName, ".zip");
+ FileUtils.copyFile(uploadedFile, destFile);
+ return destFile;
+ }
+}
Index: main/plugin-web/src/org/ametys/web/site/io/handlers/ExportSiteHandler.java
===================================================================
--- main/plugin-web/src/org/ametys/web/site/io/handlers/ExportSiteHandler.java (revision 0)
+++ main/plugin-web/src/org/ametys/web/site/io/handlers/ExportSiteHandler.java (revision 0)
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2012 Anyware Services
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ametys.web.site.io.handlers;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.lang.StringUtils;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+import org.ametys.cms.io.IOConstants;
+import org.ametys.cms.io.handlers.ExportAmetysObjectHandler;
+import org.ametys.web.repository.content.jcr.DefaultWebContent;
+import org.ametys.web.repository.site.SiteManager;
+
+/**
+ * "Proxy" handler used to export an ametys object.
+ */
+public class ExportSiteHandler extends ExportAmetysObjectHandler
+{
+ /**
+ * Ctor inheritance
+ * @param contentHandler The {@link ContentHandler} to be wrapped.
+ * @param parentNode The node from which the import/export is relative
+ */
+ public ExportSiteHandler(final ContentHandler contentHandler, Node parentNode)
+ {
+ super(contentHandler, parentNode);
+
+ _nodesToSkip.add(IOConstants.RESOURCES_NODE_NAME);
+ _nodesToSkip.add(SiteManager.ROOT_SITES);
+ _nodesToSkip.add(DefaultWebContent.ATTACHMENTS_NODE_NAME);
+ }
+
+ @Override
+ protected boolean _onNodeToSkip(String name) throws SAXException
+ {
+ // Attachments nodes are not exported if they contain an attachment.
+ // In this case, the attachment must be exported as a resource later.
+ String path = (String) _nodeStack.peek().get(PATH_PROPERTY_NAME);
+ if (DefaultWebContent.ATTACHMENTS_NODE_NAME.equals(name))
+ {
+ try
+ {
+ Node attachments = _parentNode.getParent().getNode(path);
+ if (attachments.hasNodes())
+ {
+ _resources.add(path);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ catch (RepositoryException e)
+ {
+ String msg = "Error during the export of node of type '" + DefaultWebContent.ATTACHMENTS_NODE_NAME + "' node.";
+ _logger.error(msg, e);
+ throw new SAXException(msg, e);
+ }
+ }
+ else if (IOConstants.RESOURCES_NODE_NAME.equals(name))
+ {
+ _resources.add(path);
+ }
+
+ return true;
+ }
+
+ @Override
+ protected boolean _isExternal(Item referencedItem) throws RepositoryException
+ {
+ String asbRefItemPath = referencedItem.getPath();
+
+ String siteNodePath = _parentNode.getPath();
+ String relRefItemPath = StringUtils.removeStart(asbRefItemPath, siteNodePath + '/');
+
+ // To be considered as external, referenced item should not belong to the site.
+ // It can be somewhere outside the site, or somewhere in a child site of the site
+ return !asbRefItemPath.startsWith(siteNodePath) || relRefItemPath.contains(SiteManager.ROOT_SITES + '/');
+ }
+}
Index: main/plugin-web/src/org/ametys/web/site/io/IOSiteConstants.java
===================================================================
--- main/plugin-web/src/org/ametys/web/site/io/IOSiteConstants.java (revision 0)
+++ main/plugin-web/src/org/ametys/web/site/io/IOSiteConstants.java (revision 0)
@@ -0,0 +1,29 @@
+package org.ametys.web.site.io;
+
+import org.ametys.cms.io.IOConstants;
+import org.ametys.plugins.repository.RepositoryConstants;
+
+/**
+ * Collection of constants used in the import/export of a site
+ */
+public final class IOSiteConstants
+{
+ /** Archive site filename */
+ public static final String SITE_FILE_NAME = "site" + IOConstants.SYSTEM_VIEW_EXT;
+ /** Constant for contents node name. */
+ public static final String SITEMAPS_NODE_NAME = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":sitemaps";
+
+ /** Archive attachments data folder */
+ public static final String ATTACHMENTS_DIR = "attachments/";
+ /** Archive content data folder */
+ public static final String CONTENTS_ATTACHMENTS_DIR = "contents/";
+ /** Archive page data folder */
+ public static final String PAGES_ATTACHMENTS_DIR = "pages/";
+
+ /**
+ * Private constructor to prevent instantiation of this class.
+ */
+ private IOSiteConstants()
+ {
+ }
+}
Index: main/plugin-web/src/org/ametys/web/site/io/ExportSiteArchiver.java
===================================================================
--- main/plugin-web/src/org/ametys/web/site/io/ExportSiteArchiver.java (revision 0)
+++ main/plugin-web/src/org/ametys/web/site/io/ExportSiteArchiver.java (revision 0)
@@ -0,0 +1,592 @@
+/*
+ * Copyright 2012 Anyware Services
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ametys.web.site.io;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.reading.ServiceableReader;
+import org.apache.cocoon.xml.XMLUtils;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
+import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.UnicodeExtraFieldPolicy;
+import org.apache.commons.lang.StringUtils;
+import org.apache.xml.serializer.OutputPropertiesFactory;
+import org.xml.sax.SAXException;
+
+import org.ametys.cms.content.archive.ArchiveConstants;
+import org.ametys.cms.io.IOConstants;
+import org.ametys.cms.io.IOUtils;
+import org.ametys.cms.io.exporters.AmetysExporter;
+import org.ametys.cms.io.exporters.DefaultJcrExportListener;
+import org.ametys.cms.io.exporters.LogExporter;
+import org.ametys.cms.io.exporters.ResourcesExporter;
+import org.ametys.cms.repository.Content;
+import org.ametys.plugins.repository.AmetysObjectIterable;
+import org.ametys.plugins.repository.AmetysObjectResolver;
+import org.ametys.plugins.repository.UnknownAmetysObjectException;
+import org.ametys.plugins.repository.provider.AbstractRepository;
+import org.ametys.runtime.util.IgnoreRootHandler;
+import org.ametys.web.repository.content.jcr.DefaultWebContent;
+import org.ametys.web.repository.site.Site;
+import org.ametys.web.site.io.exporters.SiteExporter;
+
+/**
+ * Generate a ZIP Archive for a given site.
+ */
+public class ExportSiteArchiver extends ServiceableReader
+{
+ /** The final transformer handler */
+ protected TransformerHandler _transformerHandler;
+
+ /** The Zip stream where entries will be written */
+ protected ZipArchiveOutputStream _zipOutput;
+
+ /** The Ametys object resolver */
+ protected AmetysObjectResolver _resolver;
+
+ /** JCR Repository */
+ protected Repository _repository;
+
+ @Override
+ public void service(ServiceManager sManager) throws ServiceException
+ {
+ super.service(sManager);
+ _resolver = (AmetysObjectResolver) sManager.lookup(AmetysObjectResolver.ROLE);
+ _repository = (Repository) sManager.lookup(AbstractRepository.ROLE);
+ }
+
+ @Override
+ public String getMimeType()
+ {
+ return "application/zip";
+ }
+
+ @Override
+ public void generate() throws IOException, SAXException, ProcessingException
+ {
+ String siteId = parameters.getParameter("id", null);
+
+ try
+ {
+ // Create a ZIP OutputStream that writes into the reader OutputStream
+ _zipOutput = new ZipArchiveOutputStream(out);
+ _zipOutput.setCreateUnicodeExtraFields(UnicodeExtraFieldPolicy.ALWAYS);
+ _zipOutput.setEncoding("UTF-8");
+
+ // Parameterize the handlers
+ _initializeTransformerHandler();
+
+ // Export the site into the ZIP Archive.
+ _exportSite(siteId);
+ _exportArchives(siteId);
+ }
+ finally
+ {
+ if (_zipOutput != null)
+ {
+ _zipOutput.finish();
+ _zipOutput.close();
+ }
+ }
+ }
+
+ private void _exportSite(String siteId) throws IOException
+ {
+ Site site = _resolver.resolveById(siteId);
+ _exportSite(site);
+ }
+
+ private void _exportSite(Site site) throws IOException
+ {
+ _exportSite(site, StringUtils.EMPTY);
+ }
+
+ private void _exportSite(Site site, final String baseEntryName) throws IOException
+ {
+ if (StringUtils.isNotEmpty(baseEntryName))
+ {
+ // Create base folder entry.
+ ZipArchiveEntry baseDir = new ZipArchiveEntry(baseEntryName);
+ _zipOutput.putArchiveEntry(baseDir);
+ _zipOutput.closeArchiveEntry();
+ }
+
+
+ SiteExporter siteExporter = new ArchiveSiteExporter(baseEntryName, site);
+
+ // Configure export
+ // site node
+ Properties props = new Properties();
+ props.put(AmetysExporter.CONTENT_HANDLER_PROPERTY, _transformerHandler);
+ siteExporter.setProperties(SiteExporter.SITE_NODE_PROPERTIES, props);
+
+ // workflows
+ props = new Properties();
+ props.put(AmetysExporter.CONTENT_HANDLER_PROPERTY, new IgnoreRootHandler(_transformerHandler));
+ siteExporter.setProperties(SiteExporter.WORKFLOWS_PROPERTIES, props);
+
+ // ametys resources
+ props = new Properties();
+ props.put(AmetysExporter.CONTENT_HANDLER_PROPERTY, _transformerHandler);
+ props.put(AmetysExporter.OUTPUT_STREAM_PROPERTY, _zipOutput);
+ props.put(AmetysExporter.LISTENER_PROPERTY, new ResourcesExporterListener(baseEntryName, site));
+ siteExporter.setProperties(SiteExporter.RESOURCES_PROPERTIES, props);
+
+ // binary properties
+ props = new Properties();
+ props.put(AmetysExporter.OUTPUT_STREAM_PROPERTY, _zipOutput);
+ props.put(AmetysExporter.LISTENER_PROPERTY, new BinaryPropertyExportListener(baseEntryName));
+ siteExporter.setProperties(SiteExporter.BINARY_PROPERTIES, props);
+
+ // logs
+ props = new Properties();
+ props.put(AmetysExporter.OUTPUT_STREAM_PROPERTY, _zipOutput);
+ siteExporter.setProperties(SiteExporter.LOGS_PROPERTIES, props);
+
+ // Run the export.
+ siteExporter.export();
+ }
+
+ private void _exportArchives(String siteId) throws IOException, ProcessingException
+ {
+ Session session = null;
+ try
+ {
+ session = _repository.login(ArchiveConstants.ARCHIVE_WORKSPACE);
+ Site site = null;
+ try
+ {
+ site = _resolver.resolveById(siteId, session);
+ AmetysObjectIterable archivedContents = site.getContents();
+ if (!archivedContents.hasNext())
+ {
+ // Nothing to export
+ return;
+ }
+ }
+ catch (UnknownAmetysObjectException e)
+ {
+ // Nothing to export
+ return;
+ }
+
+ _exportSite(site, IOConstants.ARCHIVES_DIR);
+ }
+ catch (NoSuchWorkspaceException e)
+ {
+ // ignores
+ }
+ catch (RepositoryException e)
+ {
+ getLogger().error("Unable to export the archives of site with id '" + siteId + "'", e);
+ throw new ProcessingException(e);
+ }
+ finally
+ {
+ if (session != null)
+ {
+ session.logout();
+ }
+ }
+ }
+
+ private void _initializeTransformerHandler() throws ProcessingException
+ {
+ try
+ {
+ _transformerHandler = ((SAXTransformerFactory) TransformerFactory.newInstance()).newTransformerHandler();
+ }
+ catch (Exception e)
+ {
+ getLogger().error("Unable to export the site", e);
+ throw new ProcessingException(e);
+ }
+
+ // create the format of result
+ final Properties properties = new Properties();
+ properties.put(OutputKeys.METHOD, "xml");
+ properties.put(OutputKeys.INDENT, "yes");
+ properties.put(OutputKeys.ENCODING, "UTF-8");
+ properties.put(OutputKeys.OMIT_XML_DECLARATION, "no");
+ properties.put(OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, "4");
+
+ _transformerHandler.getTransformer().setOutputProperties(properties);
+
+ // create the result where to write
+ StreamResult sResult = new StreamResult(_zipOutput);
+ _transformerHandler.setResult(sResult);
+ }
+
+ @Override
+ public void recycle()
+ {
+ this._transformerHandler = null;
+ _zipOutput = null;
+ }
+
+ /**
+ * ArchiveSiteExporter
+ */
+ protected class ArchiveSiteExporter extends SiteExporter
+ {
+ /** Prefix of the name of the created entries */
+ protected final String _baseEntryName;
+
+ /**
+ * Ctor
+ * @param baseEntryName
+ * @param site
+ */
+ public ArchiveSiteExporter(String baseEntryName, Site site)
+ {
+ super(site);
+ _baseEntryName = baseEntryName;
+ }
+
+ @Override
+ public void onSiteNodeExportStart() throws IOException
+ {
+ String entryName = _baseEntryName + IOSiteConstants.SITE_FILE_NAME;
+ _zipOutput.putArchiveEntry(new ZipArchiveEntry(entryName));
+ }
+
+ @Override
+ public void onSiteNodeExportEnd() throws IOException
+ {
+ _zipOutput.closeArchiveEntry();
+ }
+
+ @Override
+ public void onWorkflowsExportStart(Set workflowRefs) throws Exception
+ {
+ if (!workflowRefs.isEmpty())
+ {
+ String entryName = _baseEntryName + IOConstants.WORKFLOWS_FILE_NAME;
+ _zipOutput.putArchiveEntry(new ZipArchiveEntry(entryName));
+
+ // Root element creation.
+ _transformerHandler.startDocument();
+ XMLUtils.startElement(_transformerHandler, IOConstants.WORKFLOW_FILE_ROOT_NODE);
+ }
+ }
+
+ @Override
+ public void onWorkflowsExportEnd(Set workflowRefs) throws Exception
+ {
+ if (!workflowRefs.isEmpty())
+ {
+ // End of the root element
+ XMLUtils.endElement(_transformerHandler, IOConstants.WORKFLOW_FILE_ROOT_NODE);
+ _transformerHandler.endDocument();
+
+ _zipOutput.closeArchiveEntry();
+ }
+ }
+
+ @Override
+ public void onBinaryPropertiesExportStart(Map> binaryMap) throws Exception
+ {
+ if (!binaryMap.isEmpty())
+ {
+ String entryName = _baseEntryName + IOConstants.BINARY_DIR;
+ _zipOutput.putArchiveEntry(new ZipArchiveEntry(entryName));
+ _zipOutput.closeArchiveEntry();
+ }
+ }
+
+ @Override
+ public void onResourcesExportStart(Set paths) throws Exception
+ {
+ if (paths.isEmpty())
+ {
+ return;
+ }
+
+ // Classify resource by context (explorer / contents / pages)
+ Set explorerResources = new HashSet();
+ Set contentResources = new HashSet();
+ Set pageResources = new HashSet();
+ final String basePath = _site.getName() + '/';
+
+ for (String path : paths)
+ {
+ if (path.startsWith(basePath + IOConstants.RESOURCES_NODE_NAME))
+ {
+ explorerResources.add(path);
+ }
+ else if (path.startsWith(basePath + IOConstants.CONTENTS_NODE_NAME))
+ {
+ contentResources.add(path);
+ }
+ else if (path.startsWith(basePath + IOSiteConstants.SITEMAPS_NODE_NAME))
+ {
+ pageResources.add(path);
+ }
+ else
+ {
+ String msg = "Resource path do not start as expected.";
+ _logger.error(msg);
+ throw new RepositoryException(msg);
+ }
+ }
+
+ if (explorerResources.size() >= 2)
+ {
+ String msg = "There should be at most one '" + IOConstants.RESOURCES_NODE_NAME + "' node to export";
+ _logger.error(msg);
+ throw new RepositoryException(msg);
+ }
+
+ if (!explorerResources.isEmpty())
+ {
+ ZipArchiveEntry explorerEntryDir = new ZipArchiveEntry(_baseEntryName + IOConstants.EXPLORER_DIR);
+ _zipOutput.putArchiveEntry(explorerEntryDir);
+ _zipOutput.closeArchiveEntry();
+ }
+
+ if (!(contentResources.isEmpty() && pageResources.isEmpty()))
+ {
+ ZipArchiveEntry attEntryDir = new ZipArchiveEntry(_baseEntryName + IOSiteConstants.ATTACHMENTS_DIR);
+ _zipOutput.putArchiveEntry(attEntryDir);
+ _zipOutput.closeArchiveEntry();
+
+ if (!contentResources.isEmpty())
+ {
+ ZipArchiveEntry contentsEntryDir = new ZipArchiveEntry(attEntryDir.getName() + IOSiteConstants.CONTENTS_ATTACHMENTS_DIR);
+ _zipOutput.putArchiveEntry(contentsEntryDir);
+ _zipOutput.closeArchiveEntry();
+ }
+
+ if (!pageResources.isEmpty())
+ {
+ ZipArchiveEntry pagesEntryDir = new ZipArchiveEntry(attEntryDir.getName() + IOSiteConstants.PAGES_ATTACHMENTS_DIR);
+ _zipOutput.putArchiveEntry(pagesEntryDir);
+ _zipOutput.closeArchiveEntry();
+ }
+ }
+ }
+
+ @Override
+ public void onLogExportStart(Map> externalReferences) throws Exception
+ {
+ if (!externalReferences.isEmpty())
+ {
+ ZipArchiveEntry entryDir = new ZipArchiveEntry(_baseEntryName + LogExporter.LOG_FILE_NAME);
+ _zipOutput.putArchiveEntry(entryDir);
+ }
+ }
+
+ @Override
+ public void onLogExportEnd(Map> externalReferences) throws Exception
+ {
+ if (!externalReferences.isEmpty())
+ {
+ _zipOutput.closeArchiveEntry();
+ }
+ }
+ }
+
+ /**
+ * BinaryPropertyExportListener
+ */
+ protected class BinaryPropertyExportListener extends DefaultJcrExportListener
+ {
+ /** Prefix of the name of the created entries */
+ protected final String _baseEntryName;
+
+ /**
+ * Ctor
+ * @param baseEntryName
+ */
+ public BinaryPropertyExportListener(String baseEntryName)
+ {
+ _baseEntryName = baseEntryName;
+ }
+
+ @Override
+ public void onBeforeBinaryPropertyExport(Property property) throws RepositoryException, IOException
+ {
+ String entryName = _getEntryName(property);
+ _zipOutput.putArchiveEntry(new ZipArchiveEntry(entryName));
+ }
+
+ @Override
+ public void onAfterBinaryPropertyExport(Property property) throws IOException
+ {
+ _zipOutput.closeArchiveEntry();
+ }
+
+ /**
+ * Get the entry name in the archive for a given property.
+ * @param property
+ * @return The name of the entry.
+ * @throws RepositoryException
+ */
+ protected String _getEntryName(Property property) throws RepositoryException
+ {
+ final String name = property.getName();
+ final String parentIdentifier = property.getParent().getIdentifier();
+ final String hashPath = StringUtils.join(IOUtils.getHashedName(parentIdentifier), '/') + '/';
+
+ String entryPath = IOConstants.BINARY_DIR + hashPath + parentIdentifier + '/';
+ entryPath += IOUtils.encodeZipEntryName(name);
+
+ return _baseEntryName + entryPath;
+ }
+ }
+
+ /**
+ * ResourcesExporterListener
+ */
+ protected class ResourcesExporterListener extends DefaultJcrExportListener
+ {
+ /** Map used to construct the beginning of the entry name. */
+ protected final Map _addStartMapper = new HashMap();
+
+ /** Map used to exclude the start of the path of a resource in the entry name. */
+ protected final Map _excludeStartMapper = new HashMap();
+
+ /** Map used to exclude the end of the path of a resource in the entry name. */
+ protected final Map _excludeEndMapper = new HashMap();
+
+ /** Prefix of the name of the created entries */
+ protected final String _baseEntryName;
+
+ /** Base exported node */
+ protected final Node _exportedNode;
+
+ /** Resource node currently exported */
+ protected Node _currentNode;
+
+ /** Entry path of the last export resource node */
+ protected String _currentResourceEntryPath;
+
+ /**
+ * Ctor
+ * @param baseEntryName
+ * @param site
+ */
+ public ResourcesExporterListener(String baseEntryName, Site site)
+ {
+ _baseEntryName = baseEntryName;
+ _exportedNode = site.getNode();
+
+ _addStartMapper.put(IOConstants.RESOURCES_NODE_NAME, _baseEntryName + IOConstants.EXPLORER_DIR);
+ _addStartMapper.put(IOConstants.CONTENTS_NODE_NAME, _baseEntryName + IOSiteConstants.ATTACHMENTS_DIR + IOSiteConstants.CONTENTS_ATTACHMENTS_DIR);
+ _addStartMapper.put(IOSiteConstants.SITEMAPS_NODE_NAME, _baseEntryName + IOSiteConstants.ATTACHMENTS_DIR + IOSiteConstants.PAGES_ATTACHMENTS_DIR);
+
+ _excludeStartMapper.put(IOConstants.RESOURCES_NODE_NAME, IOConstants.RESOURCES_NODE_NAME);
+ _excludeStartMapper.put(IOConstants.CONTENTS_NODE_NAME, IOConstants.CONTENTS_NODE_NAME);
+ _excludeStartMapper.put(IOSiteConstants.SITEMAPS_NODE_NAME, IOSiteConstants.SITEMAPS_NODE_NAME);
+
+ _excludeEndMapper.put(IOConstants.RESOURCES_NODE_NAME, StringUtils.EMPTY);
+ _excludeEndMapper.put(IOConstants.CONTENTS_NODE_NAME, DefaultWebContent.ATTACHMENTS_NODE_NAME);
+ _excludeEndMapper.put(IOSiteConstants.SITEMAPS_NODE_NAME, DefaultWebContent.ATTACHMENTS_NODE_NAME);
+ }
+
+ @Override
+ public void onBeforeNodeExport(Node node) throws Exception
+ {
+ _currentNode = node;
+
+ // Evaluate the path of the resource in the archive.
+ _currentResourceEntryPath = _getEntryPath(node);
+
+ // Export 'resource' system view
+ ZipArchiveEntry resFile = new ZipArchiveEntry(_currentResourceEntryPath + IOConstants.RESOURCES_FILE_NAME);
+ _zipOutput.putArchiveEntry(resFile);
+ }
+
+ @Override
+ public void onAfterNodeExport(Node node) throws Exception
+ {
+ _zipOutput.closeArchiveEntry();
+ }
+
+
+ /**
+ * Find the name of the entry for the given resource node
+ * @param resourceNode the resource node
+ * @return The entry name.
+ * @throws RepositoryException
+ */
+ protected String _getEntryPath(Node resourceNode) throws RepositoryException
+ {
+ final String sitePath = _exportedNode.getPath();
+ String relPath = StringUtils.removeStart(resourceNode.getPath(), sitePath + '/');
+
+ String key = StringUtils.substringBefore(relPath, "/");
+ String excludeStart = _excludeStartMapper.get(key);
+ String excludeEnd = _excludeEndMapper.get(key);
+ String addStart = _addStartMapper.get(key);
+
+ String entryPath = StringUtils.removeEnd(StringUtils.removeStart(relPath, excludeStart), excludeEnd);
+ entryPath = addStart + StringUtils.removeStart(entryPath, "/");
+
+ return entryPath.endsWith("/") ? entryPath : entryPath + '/';
+ }
+
+ @Override
+ public void onBeforeBinaryPropertyExport(Property property) throws Exception
+ {
+ String entryName = _getEntryPath(property);
+ ZipArchiveEntry resFile = new ZipArchiveEntry(entryName);
+ _zipOutput.putArchiveEntry(resFile);
+ }
+
+ @Override
+ public void onAfterBinaryPropertyExport(Property property) throws Exception
+ {
+ _zipOutput.closeArchiveEntry();
+ }
+
+ /**
+ * Construct the name of the entry for the given property
+ * @param property
+ * @return The entry name.
+ * @throws RepositoryException
+ */
+ protected String _getEntryPath(Property property) throws RepositoryException
+ {
+ final String currentNodePath = _currentNode.getPath();
+ String relPath = StringUtils.removeStart(property.getPath(), currentNodePath + '/');
+ relPath = StringUtils.removeEnd(relPath, '/' + ResourcesExporter.JCR_DATA_PATH);
+
+ return _currentResourceEntryPath + IOConstants.RESOURCES_DIR + IOUtils.encodeZipEntryName(relPath);
+ }
+ }
+}
Index: main/plugin-web/src/org/ametys/web/site/io/exporters/SiteNodeExporter.java
===================================================================
--- main/plugin-web/src/org/ametys/web/site/io/exporters/SiteNodeExporter.java (revision 0)
+++ main/plugin-web/src/org/ametys/web/site/io/exporters/SiteNodeExporter.java (revision 0)
@@ -0,0 +1,97 @@
+package org.ametys.web.site.io.exporters;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.avalon.framework.logger.Logger;
+import org.xml.sax.ContentHandler;
+
+import org.ametys.cms.io.exporters.JcrExporter;
+import org.ametys.cms.io.exporters.WorkflowExporter;
+import org.ametys.runtime.util.LoggerFactory;
+import org.ametys.web.repository.site.Site;
+import org.ametys.web.site.io.handlers.ExportSiteHandler;
+
+/**
+ * SiteNodeExporter is used to export the node of a site. It has some getter
+ * methods that may be used by other exporters (e.g. {@link WorkflowExporter}).
+ */
+public class SiteNodeExporter extends JcrExporter
+{
+ /** logger */
+ protected final Logger _logger = LoggerFactory.getLoggerFor(SiteNodeExporter.class);
+
+ /** The site to export */
+ protected final Site _site;
+ /** The export handler */
+ protected final ExportSiteHandler _exportSiteHandler;
+
+ /**
+ * Ctor
+ * @param site
+ * @param contentHandler
+ */
+ public SiteNodeExporter(Site site, ContentHandler contentHandler)
+ {
+ _site = site;
+ _exportSiteHandler = new ExportSiteHandler(contentHandler, site.getNode());
+ }
+
+ /**
+ * Export method. This is where the job is actually done.
+ * @param skipBinary
+ * @throws Exception
+ */
+ public void export(boolean skipBinary) throws Exception
+ {
+ try
+ {
+ exportNode(_site.getNode(), _exportSiteHandler, skipBinary, true);
+ }
+ catch (Exception e)
+ {
+ // Log and re-throw
+ final String name = _site.getName();
+ _logger.error("Unable to export the site node of site '" + name + "'", e);
+ throw e;
+ }
+ }
+
+ /**
+ * Retrieves the workflow identifiers.
+ * @return the workflow identifiers
+ */
+ public Set getWorkflowRefs()
+ {
+ return _exportSiteHandler.getWorkflowRefs();
+ }
+
+ /**
+ * Retrieves the binary properties.
+ * @return the binaryProperties
+ */
+ public Map> getBinaryProperties()
+ {
+ return _exportSiteHandler.getBinaryProperties();
+ }
+
+ /**
+ * Retrieves the resources.
+ * @return the resources
+ */
+ public Set getResources()
+ {
+ return _exportSiteHandler.getResources();
+ }
+
+ /**
+ * Retrieves the external references
+ * @return the externalReferences
+ */
+ public Map> getExternalReferences()
+ {
+ return _exportSiteHandler.getExternalReferences();
+ }
+}
Index: main/plugin-web/src/org/ametys/web/site/io/exporters/SiteExporter.java
===================================================================
--- main/plugin-web/src/org/ametys/web/site/io/exporters/SiteExporter.java (revision 0)
+++ main/plugin-web/src/org/ametys/web/site/io/exporters/SiteExporter.java (revision 0)
@@ -0,0 +1,324 @@
+package org.ametys.web.site.io.exporters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.xml.sax.ContentHandler;
+
+import org.ametys.cms.io.exporters.AmetysExporter;
+import org.ametys.cms.io.exporters.BinaryPropertiesExporter;
+import org.ametys.cms.io.exporters.JcrExportListener;
+import org.ametys.cms.io.exporters.LogExporter;
+import org.ametys.cms.io.exporters.ResourcesExporter;
+import org.ametys.cms.io.exporters.WorkflowExporter;
+import org.ametys.web.repository.site.Site;
+
+/**
+ * Configurable Site Exporter. Exports its content into
+ * ContentHandler
and/or OutputStream
+ */
+public class SiteExporter extends AmetysExporter
+{
+ // Key used to configure the exporters
+ /** configuration key of the site node exporter */
+ public static final String SITE_NODE_PROPERTIES = "siteNode";
+ /** configuration key of the workflow exporter */
+ public static final String WORKFLOWS_PROPERTIES = "workflows";
+ /** configuration key of the resource exporter */
+ public static final String RESOURCES_PROPERTIES = "resources";
+ /** configuration key of the binary properties exporter */
+ public static final String BINARY_PROPERTIES = "binary";
+ /** configuration key of the logs exporter */
+ public static final String LOGS_PROPERTIES = "logs";
+
+ /** Site to export */
+ protected final Site _site;
+
+ /**
+ * Ctor
+ * @param site
+ */
+ public SiteExporter(Site site)
+ {
+ _site = site;
+ }
+
+ @Override
+ protected void _exportInternal() throws IOException
+ {
+ try
+ {
+ final Session session = _site.getNode().getSession();
+
+ // Export the site node
+ onSiteNodeExportStart();
+ SiteNodeExporter siteNodeExporter = _exportSiteNode();
+ onSiteNodeExportEnd();
+
+ // Export workflow nodes
+ onWorkflowsExportStart(siteNodeExporter.getWorkflowRefs());
+ _exportWorkflows(siteNodeExporter);
+ onWorkflowsExportEnd(siteNodeExporter.getWorkflowRefs());
+
+ // Export resources from the explorer.
+ onResourcesExportStart(siteNodeExporter.getResources());
+ _exportResources(siteNodeExporter);
+ onResourcesExportEnd(siteNodeExporter.getResources());
+
+ // Export the binary properties
+ onBinaryPropertiesExportStart(siteNodeExporter.getBinaryProperties());
+ _exportBinaryProperties(siteNodeExporter);
+ onBinaryPropertiesExportEnd(siteNodeExporter.getBinaryProperties());
+
+ // Export log file
+ onLogExportStart(siteNodeExporter.getExternalReferences());
+ _exportLog(siteNodeExporter);
+ onLogExportEnd(siteNodeExporter.getExternalReferences());
+
+ // Do not save changes.
+ session.refresh(false);
+ }
+ catch (Exception e)
+ {
+ _logger.error("Unable to export the site '" + _site.getName() + "'", e);
+ throw new IOException(e);
+ }
+ }
+
+ /**
+ * Export the node corresponding to the Site
object.
+ * @return The exporter used to export the site node, which is a {@link SiteNodeExporter}
+ * @throws Exception
+ */
+ protected SiteNodeExporter _exportSiteNode() throws Exception
+ {
+ Properties props = _getSiteNodeProperties();
+ SiteNodeExporter exporter = new SiteNodeExporter(_site, (ContentHandler) props.get(CONTENT_HANDLER_PROPERTY));
+
+ JcrExportListener listener = (JcrExportListener) props.get(LISTENER_PROPERTY);
+ exporter.setListener(listener);
+
+ exporter.export(true); // skip binary, they will be exported later.
+
+ return exporter;
+ }
+
+ /**
+ * Export the workflow nodes which references have been collected during the
+ * export of the site node.
+ *
+ * @param siteNodeExporter The exporter used to export the site node.
+ * @throws Exception
+ */
+ protected void _exportWorkflows(SiteNodeExporter siteNodeExporter) throws Exception
+ {
+ Properties props = _getWorkflowProperties();
+
+ final Session session = _site.getNode().getSession();
+ WorkflowExporter exporter = new WorkflowExporter(siteNodeExporter.getWorkflowRefs(), session, (ContentHandler) props.get(CONTENT_HANDLER_PROPERTY));
+ JcrExportListener listener = (JcrExportListener) props.get(LISTENER_PROPERTY);
+ exporter.setListener(listener);
+
+ exporter.export();
+ }
+
+ /**
+ * Export the ametys resources nodes that have been collected during the
+ * export of the site node.
+ *
+ * @param siteNodeExporter The exporter used to export the site node.
+ * @throws Exception
+ */
+ protected void _exportResources(SiteNodeExporter siteNodeExporter) throws Exception
+ {
+ Properties props = _getResourcesProperties();
+ ResourcesExporter exporter = new ResourcesExporter(_site.getNode().getParent(), siteNodeExporter.getResources(), (ContentHandler) props.get(CONTENT_HANDLER_PROPERTY), (OutputStream) props.get(OUTPUT_STREAM_PROPERTY));
+ JcrExportListener listener = (JcrExportListener) props.get(LISTENER_PROPERTY);
+ exporter.setListener(listener);
+
+ exporter.export();
+ }
+
+ /**
+ * Export the binary properties (jcr) that have been collected during the
+ * export of the site node.
+ *
+ * @param siteNodeExporter The exporter used to export the site node.
+ * @throws Exception
+ */
+ protected void _exportBinaryProperties(SiteNodeExporter siteNodeExporter) throws Exception
+ {
+ Properties props = _getBinaryPropertiesProperties();
+
+ BinaryPropertiesExporter exporter = new BinaryPropertiesExporter(_site.getNode().getParent(), siteNodeExporter.getBinaryProperties(), (OutputStream) props.get(OUTPUT_STREAM_PROPERTY));
+ JcrExportListener listener = (JcrExportListener) props.get(LISTENER_PROPERTY);
+ exporter.setListener(listener);
+
+ exporter.export();
+ }
+
+ /**
+ * Export some logs
+ * @param siteNodeExporter
+ * @throws RepositoryException
+ * @see LogExporter
+ */
+ protected void _exportLog(SiteNodeExporter siteNodeExporter) throws RepositoryException
+ {
+ Properties props = _getLogProperties();
+
+ final Session session = _site.getNode().getSession();
+ LogExporter exporter = new LogExporter(siteNodeExporter.getExternalReferences(), session, (OutputStream) props.get(OUTPUT_STREAM_PROPERTY));
+
+ exporter.export();
+ }
+
+ /**
+ * Retrieves the properties of the site node exporter.
+ * @return these configured properties
+ */
+ protected Properties _getSiteNodeProperties()
+ {
+ return _internalCheckAndGetProperties(SITE_NODE_PROPERTIES, Arrays.asList(CONTENT_HANDLER_PROPERTY), Arrays.asList(LISTENER_PROPERTY));
+ }
+
+ /**
+ * Retrieves the properties of the workflow exporter.
+ * @return these configured properties
+ */
+ protected Properties _getWorkflowProperties()
+ {
+ return _internalCheckAndGetProperties(WORKFLOWS_PROPERTIES, Arrays.asList(CONTENT_HANDLER_PROPERTY), Arrays.asList(LISTENER_PROPERTY));
+ }
+
+ /**
+ * Retrieves the properties of the ametys resources exporter.
+ * @return these configured properties
+ */
+ protected Properties _getResourcesProperties()
+ {
+ return _internalCheckAndGetProperties(RESOURCES_PROPERTIES, Arrays.asList(CONTENT_HANDLER_PROPERTY, OUTPUT_STREAM_PROPERTY), Arrays.asList(LISTENER_PROPERTY));
+ }
+
+ /**
+ * Retrieves the properties of the binary property exporter.
+ * @return these configured properties
+ */
+ protected Properties _getBinaryPropertiesProperties()
+ {
+ return _internalCheckAndGetProperties(BINARY_PROPERTIES, Arrays.asList(OUTPUT_STREAM_PROPERTY), Arrays.asList(LISTENER_PROPERTY));
+ }
+
+ /**
+ * Retrieves the properties of the log exporter.
+ * @return these configured properties
+ */
+ protected Properties _getLogProperties()
+ {
+ return _internalCheckAndGetProperties(LOGS_PROPERTIES, Arrays.asList(OUTPUT_STREAM_PROPERTY), Collections.EMPTY_LIST);
+ }
+
+ // ------- Empty listener methods ----------------------------------------<
+ /**
+ * onSiteNodeExportStart listener
+ * @throws Exception
+ */
+ public void onSiteNodeExportStart() throws Exception
+ {
+ // Nothing by default
+ }
+ /**
+ * onSiteNodeExportEnd listener
+ * @throws Exception
+ */
+ public void onSiteNodeExportEnd() throws Exception
+ {
+ // Nothing by default
+ }
+
+ /**
+ * onWorkflowsExportStart listener
+ * @param workflowRefs
+ * @throws Exception
+ */
+ public void onWorkflowsExportStart(Set workflowRefs) throws Exception
+ {
+ // Nothing by default
+ }
+ /**
+ * onWorkflowsExportEnd listener
+ * @param workflowRefs
+ * @throws Exception
+ */
+ public void onWorkflowsExportEnd(Set workflowRefs) throws Exception
+ {
+ // Nothing by default
+ }
+
+ /**
+ * onResourcesExportStart listener
+ * @param resources
+ * @throws Exception
+ */
+ public void onResourcesExportStart(Set resources) throws Exception
+ {
+ // Nothing by default
+ }
+ /**
+ * onResourcesExportEnd listener
+ * @param resources
+ * @throws Exception
+ */
+ public void onResourcesExportEnd(Set resources) throws Exception
+ {
+ // Nothing by default
+ }
+
+ /**
+ * onBinaryPropertiesExportStart listener
+ * @param binaryMap
+ * @throws Exception
+ */
+ public void onBinaryPropertiesExportStart(Map> binaryMap) throws Exception
+ {
+ // Nothing by default
+ }
+ /**
+ * onBinaryPropertiesExportEnd listener
+ * @param binaryMap
+ * @throws Exception
+ */
+ public void onBinaryPropertiesExportEnd(Map> binaryMap) throws Exception
+ {
+ // Nothing by default
+ }
+
+ /**
+ * onLogExportStart listener
+ * @param externalReferences
+ * @throws Exception
+ */
+ public void onLogExportStart(Map> externalReferences) throws Exception
+ {
+ // Nothing by default
+ }
+ /**
+ * onLogExportEnd listener
+ * @param externalReferences
+ * @throws Exception
+ */
+ public void onLogExportEnd(Map> externalReferences) throws Exception
+ {
+ // Nothing by default
+ }
+
+}
Index: main/plugin-web/src/org/apache/jackrabbit/core/ProtectedItemModifier.java
===================================================================
--- main/plugin-web/src/org/apache/jackrabbit/core/ProtectedItemModifier.java (revision 0)
+++ main/plugin-web/src/org/apache/jackrabbit/core/ProtectedItemModifier.java (revision 0)
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.ItemExistsException;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+import org.apache.jackrabbit.core.id.NodeId;
+import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
+import org.apache.jackrabbit.core.retention.RetentionManagerImpl;
+import org.apache.jackrabbit.core.security.AccessManager;
+import org.apache.jackrabbit.core.security.authorization.Permission;
+import org.apache.jackrabbit.core.security.authorization.acl.ACLEditor;
+import org.apache.jackrabbit.core.security.user.UserManagerImpl;
+import org.apache.jackrabbit.core.session.SessionOperation;
+import org.apache.jackrabbit.core.state.ChildNodeEntry;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.value.InternalValue;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
+
+import org.ametys.cms.io.importers.jcr.versions.VersionImporterModifier;
+
+/**
+ * ProtectedItemModifier
: An abstract helper class to allow classes
+ * residing outside of the core package to modify and remove protected items.
+ * The protected item definitions are required in order not to have security
+ * relevant content being changed through common item operations but forcing
+ * the usage of the corresponding APIs, which assert that implementation
+ * specific constraints are not violated.
+ */
+public abstract class ProtectedItemModifier {
+
+ private static final int DEFAULT_PERM_CHECK = -1;
+ private final int permission;
+
+ protected ProtectedItemModifier() {
+ this(DEFAULT_PERM_CHECK);
+ }
+
+ protected ProtectedItemModifier(int permission) {
+ Class extends ProtectedItemModifier> cl = getClass();
+ if (!(UserManagerImpl.class.isAssignableFrom(cl) ||
+ RetentionManagerImpl.class.isAssignableFrom(cl) ||
+ ACLEditor.class.isAssignableFrom(cl) ||
+ org.apache.jackrabbit.core.security.authorization.principalbased.ACLEditor.class.isAssignableFrom(cl) ||
+ VersionImporterModifier.class.isAssignableFrom(cl))) {
+ throw new IllegalArgumentException("Only UserManagerImpl, RetentionManagerImpl and ACLEditor may extend from the ProtectedItemModifier");
+ }
+ this.permission = permission;
+ }
+
+ protected NodeImpl addNode(NodeImpl parentImpl, Name name, Name ntName) throws RepositoryException {
+ return addNode(parentImpl, name, ntName, null);
+ }
+
+ protected NodeImpl addNode(NodeImpl parentImpl, Name name, Name ntName, NodeId nodeId) throws RepositoryException {
+ checkPermission(parentImpl, name, getPermission(true, false));
+ // validation: make sure Node is not locked or checked-in.
+ parentImpl.checkSetProperty();
+
+ NodeTypeImpl nodeType = parentImpl.sessionContext.getNodeTypeManager().getNodeType(ntName);
+ org.apache.jackrabbit.spi.commons.nodetype.NodeDefinitionImpl def = parentImpl.getApplicableChildNodeDefinition(name, ntName);
+
+ // check for name collisions
+ // TODO: improve. copied from NodeImpl
+ NodeState thisState = parentImpl.getNodeState();
+ ChildNodeEntry cne = thisState.getChildNodeEntry(name, 1);
+ if (cne != null) {
+ // there's already a child node entry with that name;
+ // check same-name sibling setting of new node
+ if (!def.allowsSameNameSiblings()) {
+ throw new ItemExistsException();
+ }
+ // check same-name sibling setting of existing node
+ NodeId newId = cne.getId();
+ NodeImpl n = (NodeImpl) parentImpl.sessionContext.getItemManager().getItem(newId);
+ if (!n.getDefinition().allowsSameNameSiblings()) {
+ throw new ItemExistsException();
+ }
+ }
+
+ return parentImpl.createChildNode(name, nodeType, nodeId);
+ }
+
+ protected Property setProperty(NodeImpl parentImpl, Name name, Value value) throws RepositoryException {
+ return setProperty(parentImpl, name, value, false);
+ }
+
+ protected Property setProperty(NodeImpl parentImpl, Name name, Value value, boolean ignorePermissions) throws RepositoryException {
+ if (!ignorePermissions) {
+ checkPermission(parentImpl, name, getPermission(false, false));
+ }
+ // validation: make sure Node is not locked or checked-in.
+ parentImpl.checkSetProperty();
+ InternalValue intVs = InternalValue.create(value, parentImpl.sessionContext);
+ return parentImpl.internalSetProperty(name, intVs);
+ }
+
+ protected Property setProperty(NodeImpl parentImpl, Name name, Value[] values) throws RepositoryException {
+ checkPermission(parentImpl, name, getPermission(false, false));
+ // validation: make sure Node is not locked or checked-in.
+ parentImpl.checkSetProperty();
+ InternalValue[] intVs = new InternalValue[values.length];
+ for (int i = 0; i < values.length; i++) {
+ intVs[i] = InternalValue.create(values[i], parentImpl.sessionContext);
+ }
+ return parentImpl.internalSetProperty(name, intVs);
+ }
+
+ protected Property setProperty(NodeImpl parentImpl, Name name, Value[] values, int type) throws RepositoryException {
+ checkPermission(parentImpl, name, getPermission(false, false));
+ // validation: make sure Node is not locked or checked-in.
+ parentImpl.checkSetProperty();
+ InternalValue[] intVs = new InternalValue[values.length];
+ for (int i = 0; i < values.length; i++) {
+ intVs[i] = InternalValue.create(values[i], parentImpl.sessionContext);
+ }
+ return parentImpl.internalSetProperty(name, intVs, type);
+ }
+
+ protected void removeItem(ItemImpl itemImpl) throws RepositoryException {
+ NodeImpl n;
+ if (itemImpl.isNode()) {
+ n = (NodeImpl) itemImpl;
+ } else {
+ n = (NodeImpl) itemImpl.getParent();
+ }
+ checkPermission(itemImpl, getPermission(itemImpl.isNode(), true));
+ // validation: make sure Node is not locked or checked-in.
+ n.checkSetProperty();
+ itemImpl.perform(new ItemRemoveOperation(itemImpl, false));
+ }
+
+ protected void markModified(NodeImpl parentImpl) throws RepositoryException {
+ parentImpl.getOrCreateTransientItemState();
+ }
+
+ protected T performProtected(SessionImpl session, SessionOperation operation) throws RepositoryException {
+ ItemValidator itemValidator = session.context.getItemValidator();
+ return itemValidator.performRelaxed(operation, ItemValidator.CHECK_CONSTRAINTS);
+ }
+
+ private void checkPermission(ItemImpl item, int perm) throws RepositoryException {
+ if (perm > Permission.NONE) {
+ SessionImpl sImpl = (SessionImpl) item.getSession();
+ AccessManager acMgr = sImpl.getAccessManager();
+
+ Path path = item.getPrimaryPath();
+ acMgr.checkPermission(path, perm);
+ }
+ }
+
+ private void checkPermission(NodeImpl node, Name childName, int perm) throws RepositoryException {
+ if (perm > Permission.NONE) {
+ SessionImpl sImpl = (SessionImpl) node.getSession();
+ AccessManager acMgr = sImpl.getAccessManager();
+
+ boolean isGranted = acMgr.isGranted(node.getPrimaryPath(), childName, perm);
+ if (!isGranted) {
+ throw new AccessDeniedException("Permission denied.");
+ }
+ }
+ }
+
+ private int getPermission(boolean isNode, boolean isRemove) {
+ if (permission < Permission.NONE) {
+ if (isNode) {
+ return (isRemove) ? Permission.REMOVE_NODE : Permission.ADD_NODE;
+ } else {
+ return (isRemove) ? Permission.REMOVE_PROPERTY : Permission.SET_PROPERTY;
+ }
+ } else {
+ return permission;
+ }
+ }
+}
\ No newline at end of file
Index: main/plugin-web/sitemap-back.xmap
===================================================================
--- main/plugin-web/sitemap-back.xmap (revision 19862)
+++ main/plugin-web/sitemap-back.xmap (working copy)
@@ -170,6 +170,8 @@
+
+
@@ -192,6 +194,10 @@
+
+
+
+
@@ -683,6 +689,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+