org.jahia.ajax.gwt.helper.ContentManagerHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.jahia.ajax.gwt.helper.ContentManagerHelper.java

Source

/**
 * ==========================================================================================
 * =                   JAHIA'S DUAL LICENSING - IMPORTANT INFORMATION                       =
 * ==========================================================================================
 *
 *                                 http://www.jahia.com
 *
 *     Copyright (C) 2002-2017 Jahia Solutions Group SA. All rights reserved.
 *
 *     THIS FILE IS AVAILABLE UNDER TWO DIFFERENT LICENSES:
 *     1/GPL OR 2/JSEL
 *
 *     1/ GPL
 *     ==================================================================================
 *
 *     IF YOU DECIDE TO CHOOSE THE GPL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS:
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 *
 *     2/ JSEL - Commercial and Supported Versions of the program
 *     ===================================================================================
 *
 *     IF YOU DECIDE TO CHOOSE THE JSEL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS:
 *
 *     Alternatively, commercial and supported versions of the program - also known as
 *     Enterprise Distributions - must be used in accordance with the terms and conditions
 *     contained in a separate written agreement between you and Jahia Solutions Group SA.
 *
 *     If you are unsure which license is appropriate for your use,
 *     please contact the sales department at sales@jahia.com.
 */
package org.jahia.ajax.gwt.helper;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.jahia.ajax.gwt.client.data.GWTJahiaContentHistoryEntry;
import org.jahia.ajax.gwt.client.data.acl.GWTJahiaNodeACE;
import org.jahia.ajax.gwt.client.data.acl.GWTJahiaNodeACL;
import org.jahia.ajax.gwt.client.data.definition.GWTJahiaNodeProperty;
import org.jahia.ajax.gwt.client.data.definition.GWTJahiaNodePropertyValue;
import org.jahia.ajax.gwt.client.data.node.GWTJahiaNode;
import org.jahia.ajax.gwt.client.service.GWTJahiaServiceException;
import org.jahia.ajax.gwt.content.server.UploadedPendingFile;
import org.jahia.api.Constants;
import org.jahia.bin.SessionNamedDataStorage;
import org.jahia.data.templates.JahiaTemplatesPackage;
import org.jahia.exceptions.JahiaException;
import org.jahia.registries.ServicesRegistry;
import org.jahia.services.content.*;
import org.jahia.services.content.decorator.JCRGroupNode;
import org.jahia.services.content.decorator.JCRSiteNode;
import org.jahia.services.content.decorator.JCRUserNode;
import org.jahia.services.content.nodetypes.ExtendedNodeType;
import org.jahia.services.history.ContentHistoryService;
import org.jahia.services.history.HistoryEntry;
import org.jahia.services.importexport.ImportExportBaseService;
import org.jahia.services.importexport.ImportExportService;
import org.jahia.services.importexport.ImportJob;
import org.jahia.services.importexport.ReferencesHelper;
import org.jahia.services.importexport.validation.*;
import org.jahia.services.scheduler.BackgroundJob;
import org.jahia.services.sites.JahiaSitesService;
import org.jahia.services.templates.JahiaTemplateManagerService;
import org.jahia.services.templates.SourceControlManagement;
import org.jahia.services.usermanager.JahiaGroupManagerService;
import org.jahia.services.usermanager.JahiaUser;
import org.jahia.services.visibility.VisibilityService;
import org.jahia.settings.SettingsBean;
import org.jahia.utils.i18n.Messages;
import org.osgi.framework.BundleException;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.*;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.query.Query;
import javax.jcr.security.Privilege;
import javax.jcr.version.VersionException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

import static org.jahia.api.Constants.JAHIAMIX_MARKED_FOR_DELETION_ROOT;

/**
 * @author rfelden
 */
public class ContentManagerHelper {

    private static final List<String> COPIED_NODE_FIELDS = Arrays.asList(GWTJahiaNode.ICON, GWTJahiaNode.TAGS,
            GWTJahiaNode.CHILDREN_INFO, "j:view", "j:width", "j:height", GWTJahiaNode.PUBLICATION_INFO,
            GWTJahiaNode.PERMISSIONS);

    private static final List<String> NEW_NODE_FIELDS = Arrays.asList(GWTJahiaNode.ICON, GWTJahiaNode.TAGS,
            GWTJahiaNode.CHILDREN_INFO, "j:view", "j:width", "j:height", GWTJahiaNode.LOCKS_INFO,
            GWTJahiaNode.SUBNODES_CONSTRAINTS_INFO);

    private static Logger logger = LoggerFactory.getLogger(ContentManagerHelper.class);

    private JahiaSitesService sitesService;
    private ContentHistoryService contentHistoryService;
    private JahiaTemplateManagerService templateManagerService;

    private NavigationHelper navigation;
    private PropertiesHelper properties;
    private VersioningHelper versioning;

    private SessionNamedDataStorage<UploadedPendingFile> fileStorage;

    public void setNavigation(NavigationHelper navigation) {
        this.navigation = navigation;
    }

    public void setProperties(PropertiesHelper properties) {
        this.properties = properties;
    }

    public void setSitesService(JahiaSitesService sitesService) {
        this.sitesService = sitesService;
    }

    public void setVersioning(VersioningHelper versioning) {
        this.versioning = versioning;
    }

    public void setContentHistoryService(ContentHistoryService contentHistoryService) {
        this.contentHistoryService = contentHistoryService;
    }

    public void setTemplateManagerService(JahiaTemplateManagerService templateManagerService) {
        this.templateManagerService = templateManagerService;
    }

    public void setFileStorage(SessionNamedDataStorage<UploadedPendingFile> fileStorage) {
        this.fileStorage = fileStorage;
    }

    /**
     * @deprecated : This method is used only to maintain compatibility with existing modules. Do not use it !
     */
    @Deprecated
    public JCRNodeWrapper addNode(JCRNodeWrapper parentNode, String name, String nodeType, List<String> mixin,
            List<GWTJahiaNodeProperty> props, Locale uiLocale) throws GWTJahiaServiceException {
        return addNode(parentNode, name, nodeType, mixin, props, uiLocale, null);
    }

    public JCRNodeWrapper addNode(JCRNodeWrapper parentNode, String name, String nodeType, List<String> mixin,
            List<GWTJahiaNodeProperty> props, Locale uiLocale, String httpSessionID)
            throws GWTJahiaServiceException {
        if (!parentNode.hasPermission(Privilege.JCR_ADD_CHILD_NODES)) {
            throw new GWTJahiaServiceException(parentNode.getPath() + " - ACCESS DENIED");
        }
        JCRNodeWrapper childNode;
        try {
            parentNode.getSession().checkout(parentNode);
            childNode = parentNode.addNode(name, nodeType);
            if (mixin != null) {
                for (String m : mixin) {
                    childNode.addMixin(m);
                }
            }
            properties.setProperties(childNode, props, httpSessionID);
        } catch (Exception e) {
            logger.error("Exception", e);
            throw new GWTJahiaServiceException(Messages.getInternalWithArguments("label.gwt.error.cannot.get.node",
                    uiLocale, e.getLocalizedMessage()));
        }
        if (childNode == null) {
            throw new GWTJahiaServiceException(
                    Messages.getInternal("label.gwt.error.node.creation.failed", uiLocale));
        }
        return childNode;
    }

    /**
     * @deprecated : This method is used only to maintain compatibility with existing modules. Do not use it !
     */
    @Deprecated
    public GWTJahiaNode createNode(String parentPath, String name, String nodeType, List<String> mixin,
            List<GWTJahiaNodeProperty> props, JCRSessionWrapper currentUserSession, Locale uiLocale,
            Map<String, String> parentNodesType, boolean forceCreation) throws GWTJahiaServiceException {
        return createNode(parentPath, name, nodeType, mixin, props, currentUserSession, uiLocale, parentNodesType,
                forceCreation, null);
    }

    public GWTJahiaNode createNode(String parentPath, String name, String nodeType, List<String> mixin,
            List<GWTJahiaNodeProperty> props, JCRSessionWrapper currentUserSession, Locale uiLocale,
            Map<String, String> parentNodesType, boolean forceCreation, String httpSessionID)
            throws GWTJahiaServiceException {
        try {
            JCRNodeWrapper parentNode = ensureParent(parentPath, currentUserSession, uiLocale, parentNodesType);

            String nodeName;

            if (name == null) {
                nodeName = findAvailableName(parentNode, nodeType.substring(nodeType.lastIndexOf(':') + 1));
            } else {
                nodeName = JCRContentUtils.escapeLocalNodeName(name);

                if (!forceCreation && checkExistence(parentPath + "/" + nodeName, currentUserSession, uiLocale)) {
                    throw new GWTJahiaServiceException(Messages.getInternalWithArguments(
                            "label.gwt.error.node.already.exists.with.name", uiLocale, nodeName));
                }
                nodeName = findAvailableName(parentNode, nodeName);
            }

            JCRNodeWrapper childNode = addNode(parentNode, nodeName, nodeType, mixin, props, uiLocale,
                    httpSessionID);
            return navigation.getGWTJahiaNode(currentUserSession.getNode(childNode.getPath()), NEW_NODE_FIELDS);
        } catch (RepositoryException e) {
            logger.error(e.getMessage(), e);
            throw new GWTJahiaServiceException(parentPath
                    + Messages.getInternal("label.gwt.error.could.not.be.accessed", uiLocale) + e.toString());
        }
    }

    private JCRNodeWrapper ensureParent(String parentPath, JCRSessionWrapper currentUserSession, Locale uiLocale,
            Map<String, String> parentNodesType)
            throws RepositoryException, GWTJahiaServiceException, PathNotFoundException, NoSuchNodeTypeException,
            LockException, VersionException, ConstraintViolationException {
        JCRNodeWrapper parentNode;
        try {
            parentNode = currentUserSession.getNode(parentPath);
        } catch (PathNotFoundException e) {
            if (parentNodesType != null) {
                // we can create the intermediate parents
                String[] pathElements = StringUtils.split(parentPath, '/');
                JCRNodeWrapper current = currentUserSession.getRootNode();
                for (String pathElement : pathElements) {
                    try {
                        current = current.getNode(pathElement);
                    } catch (PathNotFoundException pnfe) {
                        String currentPath = current.getPath() + "/" + pathElement;
                        if (!parentNodesType.containsKey(currentPath)) {
                            throw new GWTJahiaServiceException(currentPath
                                    + Messages.getInternal("label.gwt.error.could.not.be.accessed", uiLocale));
                        }
                        current = current.addNode(pathElement, parentNodesType.get(currentPath));
                    }
                }
                parentNode = current;
            } else {
                throw e;
            }
        }
        return parentNode;
    }

    public String generateNameFromTitle(List<GWTJahiaNodeProperty> props) {
        String nodeName = null;
        for (GWTJahiaNodeProperty property : props) {
            if (property != null) {
                final List<GWTJahiaNodePropertyValue> propertyValues = property.getValues();
                if (property.getName().equals("jcr:title") && propertyValues != null && propertyValues.size() > 0
                        && propertyValues.get(0).getString() != null) {
                    nodeName = JCRContentUtils.generateNodeName(propertyValues.get(0).getString());
                }
            } else {
                logger.error("found a null property");
            }
        }
        return nodeName;
    }

    public GWTJahiaNode createFolder(String parentPath, String name, JCRSessionWrapper currentUserSession,
            Locale uiLocale, String httpSessionID) throws GWTJahiaServiceException {
        JCRNodeWrapper parentNode;
        GWTJahiaNode newNode = null;
        final JCRSessionWrapper jcrSessionWrapper;
        try {
            jcrSessionWrapper = currentUserSession;
            parentNode = jcrSessionWrapper.getNode(parentPath);
            newNode = createNode(parentPath, name,
                    parentNode.isNodeType("jnt:folder") ? "jnt:folder" : "jnt:contentList", null, null,
                    currentUserSession, uiLocale, null, true, httpSessionID);
            currentUserSession.save();
        } catch (RepositoryException e) {
            logger.error(e.getMessage(), e);
            throw new GWTJahiaServiceException(parentPath
                    + Messages.getInternal("label.gwt.error.could.not.be.accessed", uiLocale) + e.toString());
        }
        return newNode;
    }

    public String findAvailableName(JCRNodeWrapper dest, String name) {
        return JCRContentUtils.findAvailableNodeName(dest, name);
    }

    public boolean checkExistence(String path, JCRSessionWrapper currentUserSession, Locale uiLocale)
            throws GWTJahiaServiceException {
        try {
            return currentUserSession.nodeExists(JCRContentUtils.escapeNodePath(path));
        } catch (RepositoryException e) {
            logger.error(e.toString(), e);
            throw new GWTJahiaServiceException(
                    Messages.getInternalWithArguments("label.gwt.error", uiLocale, e.toString()));
        }
    }

    public void move(String sourcePath, String targetPath, JCRSessionWrapper currentUserSession)
            throws RepositoryException {
        currentUserSession.move(sourcePath, targetPath);
        currentUserSession.save();
    }

    public void moveAtEnd(String sourcePath, String targetPath, JCRSessionWrapper currentUserSession)
            throws RepositoryException, GWTJahiaServiceException {
        final JCRNodeWrapper srcNode = currentUserSession.getNode(sourcePath);
        final JCRNodeWrapper targetNode = currentUserSession.getNode(targetPath);
        currentUserSession.checkout(targetNode);

        if (srcNode.getParent().getPath().equals(targetNode.getPath())) {
            if (targetNode.getPrimaryNodeType().hasOrderableChildNodes()) {
                targetNode.orderBefore(srcNode.getName(), null);
            }
        } else {
            currentUserSession.checkout(srcNode);
            currentUserSession.checkout(srcNode.getParent());
            String newname = findAvailableName(targetNode, srcNode.getName());
            currentUserSession.move(sourcePath, targetNode.getPath() + "/" + newname);
            if (targetNode.getPrimaryNodeType().hasOrderableChildNodes()) {
                targetNode.orderBefore(newname, null);
            }
        }
        currentUserSession.save();
    }

    public void moveOnTopOf(String sourcePath, String targetPath, JCRSessionWrapper currentUserSession)
            throws RepositoryException, GWTJahiaServiceException {
        final JCRNodeWrapper srcNode = currentUserSession.getNode(sourcePath);
        final JCRNodeWrapper targetNode = currentUserSession.getNode(targetPath);
        final JCRNodeWrapper targetParent = targetNode.getParent();
        currentUserSession.checkout(targetParent);
        if (srcNode.getParent().getPath().equals(targetParent.getPath())) {
            if (targetParent.getPrimaryNodeType().hasOrderableChildNodes()) {
                targetParent.orderBefore(srcNode.getName(), targetNode.getName());
            }
        } else {
            currentUserSession.checkout(srcNode);
            currentUserSession.checkout(srcNode.getParent());
            String newname = findAvailableName(targetParent, srcNode.getName());
            currentUserSession.move(sourcePath, targetParent.getPath() + "/" + newname);
            if (targetParent.getPrimaryNodeType().hasOrderableChildNodes()) {
                targetParent.orderBefore(newname, targetNode.getName());
            }
        }
        currentUserSession.save();
    }

    public void checkWriteable(List<String> paths, JahiaUser user, JCRSessionWrapper currentUserSession,
            Locale uiLocale) throws GWTJahiaServiceException {
        try {
            List<String> missedPaths = new ArrayList<String>();
            for (String aNode : paths) {
                JCRNodeWrapper node;
                try {
                    node = currentUserSession.getNode(aNode);
                } catch (RepositoryException e) {
                    logger.error(e.toString(), e);
                    missedPaths.add(aNode + Messages.getInternal("label.gwt.error.could.not.be.accessed", uiLocale)
                            + e.toString());
                    continue;
                }
                if (!node.hasPermission(Privilege.JCR_ADD_CHILD_NODES)) {
                    missedPaths.add(Messages.getInternalWithArguments("label.gwt.error.has.no.write.access.to",
                            uiLocale, user.getUsername(), node.getName()));
                } else if (node.isLocked() && !node.getLockOwner().equals(user.getUsername())) {
                    missedPaths.add(node.getName() + Messages.getInternal("label.gwt.error.locked.by", uiLocale)
                            + user.getUsername());
                }
            }
            if (missedPaths.size() > 0) {
                StringBuilder errors = new StringBuilder(
                        Messages.getInternal("label.gwt.error.following.files.could.not.be.cut", uiLocale));
                for (String err : missedPaths) {
                    errors.append("\n").append(err);
                }
                throw new GWTJahiaServiceException(errors.toString());
            }
        } catch (RepositoryException e) {
            throw new GWTJahiaServiceException(e);
        }
    }

    public List<GWTJahiaNode> copy(final List<String> pathsToCopy, final String destinationPath,
            final String newName, final boolean moveOnTop, final boolean cut, final boolean reference,
            boolean allLanguages, JCRSessionWrapper currentUserSession) throws GWTJahiaServiceException {
        return copy(pathsToCopy, destinationPath, newName, moveOnTop, cut, reference, null, allLanguages,
                currentUserSession, currentUserSession.getLocale());
    }

    public List<GWTJahiaNode> copy(final List<String> pathsToCopy, final String destinationPath,
            final String newName, final boolean moveOnTop, final boolean cut, final boolean reference,
            final List<String> childNodeTypesToSkip, boolean allLanguages, JCRSessionWrapper currentUserSession,
            final Locale uiLocale) throws GWTJahiaServiceException {
        final List<String> missedPaths = new ArrayList<String>();
        final List<GWTJahiaNode> res = new ArrayList<GWTJahiaNode>();

        // perform a check to prevent pasting content to itself or its children
        for (Iterator<String> iterator = pathsToCopy.iterator(); iterator.hasNext();) {
            String toCopy = iterator.next();
            if (destinationPath.equals(toCopy) || destinationPath.startsWith(toCopy + "/")) {
                missedPaths.add(Messages.getInternalWithArguments("failure.paste.cannot.paste",
                        "Content {0} cannot be pasted into {1}", uiLocale, toCopy, destinationPath));
                iterator.remove();
            }
        }
        if (!missedPaths.isEmpty() && pathsToCopy.isEmpty()) {
            throw new GWTJahiaServiceException(StringUtils.join(missedPaths, "\n"));
        }

        try {
            JCRCallback<List<String>> callback = new JCRCallback<List<String>>() {
                public List<String> doInJCR(JCRSessionWrapper session) throws RepositoryException {

                    List<String> res = new ArrayList<String>();

                    final JCRNodeWrapper targetParent;
                    JCRNodeWrapper targetNode;

                    targetNode = session.getNode(destinationPath);

                    if (moveOnTop) {
                        targetParent = targetNode.getParent();
                    } else {
                        targetParent = targetNode;
                    }

                    for (String aNode : pathsToCopy) {
                        JCRNodeWrapper node = session.getNode(aNode);
                        String name = newName != null ? newName : node.getName();
                        try {
                            name = findAvailableName(targetParent, name);
                            if (targetParent.hasPermission("jcr:addChildNodes") && !targetParent.isLocked()) {
                                final JCRNodeWrapper copy = doPaste(targetParent, node, name, cut, reference,
                                        childNodeTypesToSkip);

                                if (moveOnTop && targetParent.getPrimaryNodeType().hasOrderableChildNodes()) {
                                    targetParent.orderBefore(name, targetNode.getName());
                                }
                                session.save();
                                res.add(copy.getIdentifier());
                            } else {
                                missedPaths.add(
                                        "File " + name + " could not be referenced in " + targetParent.getPath());
                            }
                        } catch (RepositoryException e) {
                            logger.error("Exception", e);
                            if (cut) {
                                missedPaths.add(Messages.getInternalWithArguments("failure.cut.cannot.cut",
                                        uiLocale, name, targetParent.getPath(), node.getPath(),
                                        session.getUser().getName()));
                            } else {
                                missedPaths.add(
                                        "File " + name + " could not be referenced in " + targetParent.getPath());
                            }
                        } catch (JahiaException e) {
                            logger.error("Exception", e);
                            missedPaths
                                    .add("File " + name + " could not be referenced in " + targetParent.getPath());
                        }
                    }

                    return res;
                }
            };

            List<String> uuids;
            if (allLanguages) {
                uuids = JCRTemplate.getInstance().doExecute(currentUserSession.getUser(),
                        currentUserSession.getWorkspace().getName(), null, callback);
            } else {
                uuids = callback.doInJCR(currentUserSession);
            }

            if (missedPaths.size() > 0) {
                StringBuilder errors = new StringBuilder();
                if (cut) {
                    for (String err : missedPaths) {
                        errors.append("\n").append(err);
                    }
                } else {
                    errors.append("The following files could not have their reference pasted:");
                    for (String err : missedPaths) {
                        errors.append("\n").append(err);
                    }
                }
                throw new GWTJahiaServiceException(errors.toString());
            }

            for (String uuid : uuids) {
                res.add(navigation.getGWTJahiaNode(currentUserSession.getNodeByUUID(uuid), COPIED_NODE_FIELDS));
            }
        } catch (RepositoryException e) {
            throw new GWTJahiaServiceException(e);
        }
        return res;
    }

    private JCRNodeWrapper doPaste(JCRNodeWrapper targetNode, JCRNodeWrapper node, String name, boolean cut,
            boolean reference, List<String> childNodeTypesToSkip) throws RepositoryException, JahiaException {
        targetNode.checkout();
        if (cut) {
            node.getSession().checkout(node);
            node.getParent().getSession().checkout(node.getParent());
            targetNode.getSession().move(node.getPath(), targetNode.getPath() + "/" + name);
        } else if (reference) {
            targetNode.getSession().checkout(targetNode);
            if (targetNode.getPrimaryNodeTypeName().equals("jnt:members")) {
                if (node.getPrimaryNodeTypeName().equals("jnt:user")) {
                    Node member = targetNode.addNode(name, Constants.JAHIANT_MEMBER);
                    member.setProperty("j:member", node.getIdentifier());
                } else if (node.getPrimaryNodeTypeName().equals("jnt:group")) {
                    Node node1 = node.getParent().getParent();
                    String id = "systemsite";
                    if (node1 != null
                            && node1.getPrimaryNodeType().getName().equals(Constants.JAHIANT_VIRTUALSITE)) {
                        id = sitesService.getSiteByKey(node1.getName()).getSiteKey();
                    }
                    name += ("___" + id);
                    Node member = targetNode.addNode(name, Constants.JAHIANT_MEMBER);
                    member.setProperty("j:member", node.getIdentifier());
                }
            } else {
                Node ref = targetNode.addNode(name, "jnt:contentReference");
                ref.setProperty(Constants.NODE, node.getIdentifier());
            }
        } else {
            JCRSiteNode sourceSite = node.getResolveSite();
            JCRSiteNode targetSite = targetNode.getResolveSite();
            if (!sourceSite.equals(targetSite)) {
                JCRSessionWrapper session = node.getSession();
                Query q = session.getWorkspace().getQueryManager().createQuery(
                        "select * from [jnt:template] as t where isdescendantnode(t, ['"
                                + JCRContentUtils.sqlEncode(sourceSite.getPath()) + "/templates'])",
                        Query.JCR_SQL2);
                NodeIterator ni = q.execute().getNodes();
                while (ni.hasNext()) {
                    JCRNodeWrapper next = (JCRNodeWrapper) ni.next();
                    try {
                        session.getUuidMapping().put(next.getIdentifier(),
                                session.getNode(targetSite.getPath()
                                        + StringUtils.substringAfter(next.getPath(), sourceSite.getPath()))
                                        .getIdentifier());
                    } catch (RepositoryException e) {
                        logger.debug("No matching template for copy", e);
                    }
                }
            }
            if (childNodeTypesToSkip == null) {
                node.copy(targetNode, name, true, null, SettingsBean.getInstance().getImportMaxBatch());
            } else {
                String newName = JCRContentUtils.findAvailableNodeName(targetNode, name);
                JCRNodeWrapper newNode = targetNode.addNode(newName, node.getPrimaryNodeTypeName());
                for (ExtendedNodeType mixin : node.getMixinNodeTypes()) {
                    if (!Constants.forbiddenMixinToCopy.contains(mixin.getName())) {
                        newNode.addMixin(mixin.getName());
                    }
                }
                Map<String, List<String>> references = new HashMap<String, List<String>>();
                node.copyProperties(newNode, references);
                ReferencesHelper.resolveCrossReferences(node.getSession(), references, false);

                for (JCRNodeWrapper childNode : node.getNodes()) {
                    childNode.copy(newNode, childNode.getName(), true, childNodeTypesToSkip,
                            SettingsBean.getInstance().getImportMaxBatch());
                }
            }
        }
        return targetNode.getNode(name);
    }

    public void deletePaths(List<String> paths, boolean permanentlyDelete, String comment, JahiaUser user,
            JCRSessionWrapper currentUserSession, Locale uiLocale) throws GWTJahiaServiceException {
        List<String> missedPaths = new ArrayList<String>();
        for (String path : paths) {
            JCRNodeWrapper nodeToDelete = null;
            try {
                nodeToDelete = currentUserSession.getNode(path);
                if (!currentUserSession.getUserNode().isRoot() && nodeToDelete.isLocked()
                        && !nodeToDelete.getLockOwner().equals(user.getUsername())) {
                    if (nodeToDelete.isNodeType(JAHIAMIX_MARKED_FOR_DELETION_ROOT)
                            && nodeToDelete.hasPermission(Privilege.JCR_REMOVE_NODE)) {
                        nodeToDelete.unmarkForDeletion();
                    } else {
                        missedPaths.add(nodeToDelete.getPath() + " - locked by " + nodeToDelete.getLockOwner());
                    }
                }
                if (!nodeToDelete.hasPermission(Privilege.JCR_REMOVE_NODE)) {
                    missedPaths.add(nodeToDelete.getPath() + " - ACCESS DENIED");
                } else if (!getRecursedLocksAndFileUsages(nodeToDelete, missedPaths, user.getUsername())) {
                    if (!permanentlyDelete && supportsMarkingForDeletion(nodeToDelete)) {
                        nodeToDelete.markForDeletion(comment);
                    } else {
                        nodeToDelete.getParent().getSession().checkout(nodeToDelete.getParent());
                        nodeToDelete.remove();
                    }

                    nodeToDelete.saveSession();
                }
            } catch (PathNotFoundException e) {
                missedPaths.add(path + Messages.getInternal("label.gwt.error.could.not.be.accessed", uiLocale)
                        + e.toString());
            } catch (AccessDeniedException e) {
                missedPaths.add((nodeToDelete != null ? nodeToDelete.getPath() : "") + " - ACCESS DENIED");
            } catch (ReferentialIntegrityException e) {
                missedPaths.add((nodeToDelete != null ? nodeToDelete.getPath() : "") + " - is in use");
            } catch (RepositoryException e) {
                logger.error(e.getMessage(), e);
                throw new GWTJahiaServiceException(e);
            }
        }
        if (missedPaths.size() > 0) {
            StringBuilder errors = new StringBuilder(Messages.getInternal("label.error.nodes.not.deleted", uiLocale,
                    "The following nodes could not be deleted:"));
            for (String err : missedPaths) {
                errors.append("\n").append(err);
            }
            throw new GWTJahiaServiceException(errors.toString());
        }
    }

    public void undeletePaths(List<String> paths, JahiaUser user, JCRSessionWrapper currentUserSession,
            Locale uiLocale) throws GWTJahiaServiceException {
        List<String> missedPaths = new ArrayList<String>();
        for (String path : paths) {
            JCRNodeWrapper nodeToUndelete = null;
            try {
                nodeToUndelete = currentUserSession.getNode(path);
                if (!nodeToUndelete.hasPermission(Privilege.JCR_REMOVE_NODE)) {
                    missedPaths.add(nodeToUndelete.getPath() + " - ACCESS DENIED");
                } else {
                    nodeToUndelete.unmarkForDeletion();
                    nodeToUndelete.saveSession();
                }
            } catch (PathNotFoundException e) {
                missedPaths.add(path + Messages.getInternal("label.gwt.error.could.not.be.accessed", uiLocale)
                        + e.toString());
            } catch (AccessDeniedException e) {
                missedPaths.add((nodeToUndelete != null ? nodeToUndelete.getPath() : "") + " - ACCESS DENIED");
            } catch (RepositoryException e) {
                logger.error(e.getMessage(), e);
                throw new GWTJahiaServiceException(e);
            }
        }
        if (missedPaths.size() > 0) {
            StringBuilder errors = new StringBuilder(Messages.getInternal("label.error.nodes.not.deleted", uiLocale,
                    "The following nodes could not be undeleted:"));
            for (String err : missedPaths) {
                errors.append("\n").append(err);
            }
            throw new GWTJahiaServiceException(errors.toString());
        }
    }

    private boolean supportsMarkingForDeletion(JCRNodeWrapper nodeToDelete) throws RepositoryException {
        return nodeToDelete.canMarkForDeletion();
    }

    public GWTJahiaNode rename(String path, String newName, JCRSessionWrapper currentUserSession, Locale uiLocale)
            throws GWTJahiaServiceException {
        JCRNodeWrapper node;
        try {
            node = currentUserSession.getNode(path);
        } catch (RepositoryException e) {
            logger.error(e.toString(), e);
            throw new GWTJahiaServiceException(
                    path + Messages.getInternal("label.gwt.error.could.not.be.accessed", uiLocale) + e.toString());
        }
        try {
            if (node.isLocked() && !node.getLockOwner().equals(currentUserSession.getUser().getName())) {
                throw new GWTJahiaServiceException(
                        node.getName() + Messages.getInternal("label.gwt.error.locked.by", uiLocale)
                                + currentUserSession.getUser().getName());
            } else if (!node.hasPermission(Privilege.JCR_WRITE)) {
                throw new GWTJahiaServiceException(node.getName() + " - ACCESS DENIED");
            } else if (!node.rename(JCRContentUtils.escapeLocalNodeName(newName))) {
                throw new GWTJahiaServiceException(Messages.getInternalWithArguments(
                        "label.gwt.error.could.not.rename", uiLocale, node.getName(), newName));
            }
        } catch (ItemExistsException e) {
            throw new GWTJahiaServiceException(
                    Messages.getInternalWithArguments("label.gwt.error.already.exists", uiLocale, newName));
        } catch (ConstraintViolationException e) {
            throw new GWTJahiaServiceException(Messages.getInternalWithArguments("label.gwt.error.could.not.rename",
                    uiLocale, node.getName(), newName) + ". " + Messages.getInternal("label.cause", uiLocale) + ": "
                    + e.getLocalizedMessage());
        } catch (RepositoryException e) {
            logger.error(e.toString(), e);
            throw new GWTJahiaServiceException(Messages.getInternalWithArguments("label.gwt.error.could.not.rename",
                    uiLocale, node.getName(), newName));
        }
        try {
            node.saveSession();
            return navigation.getGWTJahiaNode(currentUserSession.getNode(node.getPath()), NEW_NODE_FIELDS);
        } catch (RepositoryException e) {
            logger.error(e.getMessage(), e);
            throw new GWTJahiaServiceException("Could not save file " + node.getName() + " into " + newName);
        }
    }

    public void importContent(String parentPath, String fileKey, JCRSessionWrapper session, Locale uiLocale,
            String httpSessionID) throws GWTJahiaServiceException {
        importContent(parentPath, fileKey, false, session, uiLocale, httpSessionID);
    }

    public void importContent(String parentPath, String fileKey, boolean replaceContent, JCRSessionWrapper session,
            Locale uiLocale, String httpSessionID) throws GWTJahiaServiceException {

        try {

            UploadedPendingFile item = fileStorage.getRequired(httpSessionID, fileKey);
            try {

                ImportExportService importExport = ServicesRegistry.getInstance().getImportExportService();
                JCRNodeWrapper parent = session.getNode(parentPath);
                JCRSiteNode resolveSite = parent.getResolveSite();
                String detectedContentType = ImportExportBaseService.detectImportContentType(item.getContentType(),
                        fileKey);
                InputStream itemStream = null;
                ValidationResults results;
                try {
                    itemStream = item.getContentStream();
                    results = importExport.validateImportFile(session, itemStream, detectedContentType,
                            resolveSite != null ? resolveSite.getInstalledModules() : null);
                } finally {
                    IOUtils.closeQuietly(itemStream);
                }

                if (results.isSuccessful()) {
                    try {
                        // First let's copy the file in the JCR
                        JCRNodeWrapper privateFilesFolder = JCRContentUtils.getInstance()
                                .getUserPrivateFilesFolder(session);
                        String importFilename = "import" + Math.random() * 1000;
                        itemStream = item.getContentStream();
                        JCRNodeWrapper jcrNodeWrapper = privateFilesFolder.uploadFile(importFilename, itemStream,
                                detectedContentType);
                        session.save();
                        // let's schedule an import job.
                        JobDetail jobDetail = BackgroundJob
                                .createJahiaJob(Messages.getInternal("import.file", uiLocale, "Import file") + " "
                                        + FilenameUtils.getName(fileKey), ImportJob.class);
                        JobDataMap jobDataMap;
                        jobDataMap = jobDetail.getJobDataMap();
                        jobDataMap.put(ImportJob.DESTINATION_PARENT_PATH, parentPath);
                        jobDataMap.put(ImportJob.URI, jcrNodeWrapper.getPath());
                        jobDataMap.put(ImportJob.FILENAME, FilenameUtils.getName(fileKey));
                        jobDataMap.put(ImportJob.REPLACE_CONTENT, replaceContent);
                        ServicesRegistry.getInstance().getSchedulerService().scheduleJobNow(jobDetail);
                    } finally {
                        IOUtils.closeQuietly(itemStream);
                    }
                } else {
                    StringBuilder buffer = new StringBuilder();
                    for (ValidationResult result : results.getResults()) {
                        if (!result.isSuccessful()) {
                            if (result instanceof MissingModulesValidationResult) {
                                MissingModulesValidationResult missingModule = ((MissingModulesValidationResult) result);
                                if (missingModule.isTargetTemplateSetPresent()) {
                                    buffer.append(
                                            Messages.getInternalWithArguments("failure.import.missingTemplateSet",
                                                    uiLocale, missingModule.getTargetTemplateSet()));
                                }
                                if (!missingModule.getMissingModules().isEmpty()) {
                                    buffer.append(Messages.getInternalWithArguments("failure.import.missingModules",
                                            uiLocale, missingModule.getMissingModules().size()))
                                            .append(missingModule.getMissingModules());
                                }
                            } else if (result instanceof MissingNodetypesValidationResult) {
                                buffer.append(Messages.getInternalWithArguments("failure.import.missingNodetypes",
                                        uiLocale, ((MissingNodetypesValidationResult) result).getMissingNodetypes(),
                                        ((MissingNodetypesValidationResult) result).getMissingMixins()));
                            } else if (result instanceof MissingTemplatesValidationResult) {
                                MissingTemplatesValidationResult missingTemplates = ((MissingTemplatesValidationResult) result);
                                buffer.append(Messages.getInternalWithArguments("failure.import.missingTemplates",
                                        uiLocale, missingTemplates.getMissingTemplates().size()))
                                        .append(missingTemplates.getMissingTemplates().keySet());
                            }
                        }
                    }
                    throw new GWTJahiaServiceException(buffer.toString());
                }
            } finally {
                item.close();
            }
        } catch (GWTJahiaServiceException e) {
            throw e;
        } catch (Exception e) {
            logger.error("Error when importing", e);
            throw new GWTJahiaServiceException(Messages.getInternalWithArguments("label.gwt.error.could.not.import",
                    uiLocale, e.getLocalizedMessage()));
        }
    }

    public GWTJahiaNodeACL getACL(String path, boolean newAcl, JCRSessionWrapper currentUserSession,
            Locale uiLocale) throws GWTJahiaServiceException {
        JCRNodeWrapper node;
        try {
            node = currentUserSession.getNode(path);
        } catch (RepositoryException e) {
            logger.error(e.toString(), e);
            throw new GWTJahiaServiceException(
                    path + Messages.getInternal("label.gwt.error.could.not.be.accessed", uiLocale) + e.toString());
        }
        Map<String, List<String[]>> m = node.getAclEntries();

        GWTJahiaNodeACL acl = new GWTJahiaNodeACL();

        try {
            Map<String, List<JCRNodeWrapper>> roles = node.getAvailableRoles();
            Map<String, List<String>> dependencies = new HashMap<String, List<String>>();

            Map<String, List<String>> availablePermissions = new HashMap<String, List<String>>();
            Set<String> allAvailablePermissions = new HashSet<String>();
            Map<String, String> labels = new HashMap<String, String>();
            Map<String, String> tooltips = new HashMap<String, String>();

            for (Map.Entry<String, List<JCRNodeWrapper>> entry : roles.entrySet()) {
                availablePermissions.put(entry.getKey(), new ArrayList<String>());
                for (JCRNodeWrapper nodeWrapper : entry.getValue()) {
                    String nodeName = nodeWrapper.getName();
                    allAvailablePermissions.add(nodeName);
                    availablePermissions.get(entry.getKey()).add(nodeName);
                    String label = nodeWrapper.getDisplayableName();
                    labels.put(nodeName, StringUtils.isEmpty(label) ? nodeName : label);
                    if (nodeWrapper.hasProperty("jcr:description")) {
                        tooltips.put(nodeName, nodeWrapper.getProperty("jcr:description").getString());
                    }
                    List<String> d = new ArrayList<String>();
                    if (nodeWrapper.hasProperty("j:dependencies")) {
                        for (Value value : nodeWrapper.getProperty("j:dependencies").getValues()) {
                            d.add(((JCRValueWrapper) value).getNode().getName());
                        }
                    }
                    dependencies.put(nodeName, d);
                }
            }
            acl.setAvailableRoles(availablePermissions);
            acl.setRolesLabels(labels);
            acl.setRolesTooltips(tooltips);
            acl.setRoleDependencies(dependencies);

            List<GWTJahiaNodeACE> aces = new ArrayList<GWTJahiaNodeACE>();
            Map<String, GWTJahiaNodeACE> map = new HashMap<String, GWTJahiaNodeACE>();

            JahiaGroupManagerService groupManagerService = ServicesRegistry.getInstance()
                    .getJahiaGroupManagerService();
            for (String principal : m.keySet()) {
                GWTJahiaNodeACE ace = new GWTJahiaNodeACE();
                ace.setPrincipalType(principal.charAt(0));
                ace.setPrincipal(principal.substring(2)); // we set this even if we can't lookup the principal
                if (ace.getPrincipalType() == 'g') {
                    JCRGroupNode g = groupManagerService.lookupGroup(node.getResolveSite().getSiteKey(),
                            ace.getPrincipal());
                    if (g == null) {
                        g = groupManagerService.lookupGroup(null, ace.getPrincipal());
                    }
                    if (g != null) {
                        ace.setHidden(g.isHidden());
                        ace.setPrincipalKey(g.getPath());
                        String groupName = g.getDisplayableName(uiLocale);
                        ace.setPrincipalDisplayName(groupName);
                    } else {
                        continue;
                    }
                } else {
                    JCRUserNode u = ServicesRegistry.getInstance().getJahiaUserManagerService()
                            .lookupUser(ace.getPrincipal(), node.getResolveSite().getName());
                    if (u != null) {
                        ace.setPrincipalKey(u.getPath());
                        String userName = u.getDisplayableName(uiLocale);
                        ace.setPrincipalDisplayName(userName);
                    } else {
                        continue;
                    }
                }

                map.put(principal, ace);

                List<String[]> st = m.get(principal);
                Map<String, Boolean> perms = new HashMap<String, Boolean>();
                Map<String, Boolean> inheritedPerms = new HashMap<String, Boolean>();
                String inheritedFrom = null;
                for (String[] strings : st) {
                    String pathFrom = strings[0];
                    String aclType = strings[1];
                    if (aclType.equals("GRANT") || aclType.equals("DENY")) {
                        String role = strings[2];
                        if (!newAcl && path.equals(pathFrom)) {
                            perms.put(role, aclType.equals("GRANT"));
                        } else if (!inheritedPerms.containsKey(role)) {
                            if (inheritedFrom == null || inheritedFrom.length() < pathFrom.length()) {
                                inheritedFrom = pathFrom;
                            }
                            inheritedPerms.put(role, aclType.equals("GRANT"));
                        }
                    }
                }
                if (!CollectionUtils.intersection(allAvailablePermissions, inheritedPerms.keySet()).isEmpty()
                        || !CollectionUtils.intersection(allAvailablePermissions, perms.keySet()).isEmpty()) {
                    aces.add(ace);
                    ace.setInheritedFrom(inheritedFrom);
                    ace.setInheritedRoles(inheritedPerms);
                    ace.setRoles(perms);
                    ace.setInherited(perms.isEmpty());
                }
            }

            boolean aclInheritanceBreak = node.getAclInheritanceBreak();
            acl.setBreakAllInheritance(aclInheritanceBreak);

            if (aclInheritanceBreak) {
                m = node.getParent().getAclEntries();
                for (String principal : m.keySet()) {
                    GWTJahiaNodeACE ace = map.get(principal);
                    if (ace == null) {
                        ace = new GWTJahiaNodeACE();
                        map.put(principal, ace);
                        aces.add(ace);
                        ace.setPrincipalType(principal.charAt(0));
                        ace.setPrincipal(principal.substring(2)); // we set this even if we can't lookup the principal
                        if (ace.getPrincipalType() == 'g') {
                            JCRGroupNode g = groupManagerService.lookupGroup(node.getResolveSite().getSiteKey(),
                                    ace.getPrincipal());
                            if (g == null) {
                                g = groupManagerService.lookupGroup(null, ace.getPrincipal());
                            }
                            if (g != null) {
                                ace.setHidden(g.isHidden());
                                String groupName = g.getDisplayableName(uiLocale);
                                ace.setPrincipalKey(g.getPath());
                                ace.setPrincipalDisplayName(groupName);
                            }
                        } else {
                            JCRUserNode u = ServicesRegistry.getInstance().getJahiaUserManagerService()
                                    .lookupUser(ace.getPrincipal(), node.getResolveSite().getName());
                            if (u != null) {
                                ace.setPrincipalKey(u.getPath());
                                String userName = u.getDisplayableName(uiLocale);
                                ace.setPrincipalDisplayName(userName);
                            }
                        }
                        ace.setRoles(new HashMap<String, Boolean>());
                    }
                    Map<String, Boolean> inheritedPerms = new HashMap<String, Boolean>();

                    List<String[]> st = m.get(principal);
                    String inheritedFrom = null;
                    for (String[] strings : st) {
                        String pathFrom = strings[0];
                        String aclType = strings[1];
                        if (aclType.equals("GRANT") || aclType.equals("DENY")) {
                            String role = strings[2];
                            if (!inheritedPerms.containsKey(role)) {
                                if (inheritedFrom == null || inheritedFrom.length() < pathFrom.length()) {
                                    inheritedFrom = pathFrom;
                                }
                                inheritedPerms.put(role, aclType.equals("GRANT"));
                            }
                        }
                    }

                    ace.setInheritedFrom(inheritedFrom);
                    ace.setInheritedRoles(inheritedPerms);
                }
            }
            acl.setAce(aces);

        } catch (RepositoryException e) {
            logger.error(e.getMessage(), e);
        }
        return acl;
    }

    public void setACL(String uuid, GWTJahiaNodeACL acl, JCRSessionWrapper currentUserSession)
            throws GWTJahiaServiceException {
        JCRNodeWrapper node;
        try {
            node = currentUserSession.getNodeByUUID(uuid);
            if (!node.isCheckedOut()) {
                currentUserSession.checkout(node);
            }
        } catch (RepositoryException e) {
            logger.error(e.toString(), e);
            throw new GWTJahiaServiceException(uuid + " could not be accessed :\n" + e.toString());
        }
        GWTJahiaNodeACL oldAcl = getACL(node.getPath(), false, currentUserSession, null);
        if (oldAcl.equals(acl)) {
            return;
        }
        try {
            Map<String, GWTJahiaNodeACE> oldPrincipals = new HashMap<String, GWTJahiaNodeACE>();
            for (GWTJahiaNodeACE ace : oldAcl.getAce()) {
                if (!ace.getRoles().isEmpty()) {
                    oldPrincipals.put(ace.getPrincipalType() + ":" + ace.getPrincipal(), ace);
                }
            }
            for (GWTJahiaNodeACE ace : acl.getAce()) {
                String user = ace.getPrincipalType() + ":" + ace.getPrincipal();
                if (!ace.getRoles().isEmpty()) {
                    Map<String, String> perms = new HashMap<String, String>();
                    GWTJahiaNodeACE oldAce = oldPrincipals.get(user);
                    if (!ace.equals(oldAce)) {
                        for (Map.Entry<String, Boolean> entry : ace.getRoles().entrySet()) {
                            if (entry.getValue().equals(Boolean.TRUE)
                                    && (!Boolean.TRUE.equals(ace.getInheritedRoles().get(entry.getKey()))
                                            || acl.isBreakAllInheritance())) {
                                perms.put(entry.getKey(), "GRANT");
                            } else if (entry.getValue().equals(Boolean.FALSE)
                                    && (Boolean.TRUE.equals(ace.getInheritedRoles().get(entry.getKey()))
                                            || acl.isBreakAllInheritance())) {
                                perms.put(entry.getKey(), "DENY");
                            } else {
                                perms.put(entry.getKey(), "REMOVE");
                            }
                        }
                        for (Map.Entry<String, Boolean> entry : ace.getInheritedRoles().entrySet()) {
                            if (!entry.getValue() && !perms.containsKey(entry.getKey())) {
                                perms.put(entry.getKey(), "REMOVE");
                            }
                        }
                        node.changeRoles(user, perms);
                    }
                }
            }
            node.setAclInheritanceBreak(acl.isBreakAllInheritance());
        } catch (RepositoryException e) {
            logger.error("Error while saving acl on node " + node.getPath(), e);
            throw new GWTJahiaServiceException("Could not save acl on node " + node.getPath());
        }
    }

    private boolean getRecursedLocksAndFileUsages(JCRNodeWrapper nodeToDelete, List<String> lockedNodes,
            String username) throws GWTJahiaServiceException {
        try {
            for (NodeIterator iterator = nodeToDelete.getNodes(); iterator.hasNext();) {
                JCRNodeWrapper child = (JCRNodeWrapper) iterator.next();
                getRecursedLocksAndFileUsages(child, lockedNodes, username);
                if (lockedNodes.size() >= 10) {
                    // do not check further
                    return true;
                }
            }
            if (nodeToDelete.isLocked() && !nodeToDelete.getLockOwner().equals(username)) {
                Set<JCRNodeLockType> lockTypes = JCRContentUtils.getLockTypes(nodeToDelete.getLockInfos());
                if (lockTypes.size() != 1 || !lockTypes.contains(JCRNodeLockType.DELETION)) {
                    lockedNodes.add(nodeToDelete.getPath() + " - locked by " + nodeToDelete.getLockOwner());
                }
            }
        } catch (RepositoryException e) {
            throw new GWTJahiaServiceException("Cannot get lock information", e);
        }
        return !lockedNodes.isEmpty();
    }

    public void clearAllLocks(String path, boolean processChildNodes, JCRSessionWrapper currentUserSession,
            Locale uiLocale) throws GWTJahiaServiceException {
        try {
            if (currentUserSession.getUserNode().isRoot()) {
                JCRContentUtils.clearAllLocks(path, processChildNodes, currentUserSession.getWorkspace().getName());
            } else {
                JCRNodeWrapper node = currentUserSession.getNode(path);
                Map<String, List<String>> lockInfos = node.getLockInfos();
                List<String> locks = lockInfos.get(null);
                if (locks != null) {
                    for (String lock : locks) {
                        if (StringUtils.substringBefore(lock, ":").equals(currentUserSession.getUserID())) {
                            node.unlock(StringUtils.substringAfter(lock, ":"));
                        }
                    }
                }
            }
        } catch (RepositoryException e) {
            logger.error("Repository error when clearing all locks on node " + path, e);
            throw new GWTJahiaServiceException(
                    Messages.getInternalWithArguments("label.gwt.error.when.clearing.all.locks.on.node", uiLocale,
                            path, currentUserSession.getUser().getUserKey()));
        }
    }

    public void setLock(List<String> paths, boolean toLock, JCRSessionWrapper currentUserSession)
            throws GWTJahiaServiceException {
        JahiaUser user = currentUserSession.getUser();
        List<String> missedPaths = new ArrayList<String>();
        List<JCRNodeWrapper> nodes = new ArrayList<JCRNodeWrapper>();
        for (String path : paths) {
            JCRNodeWrapper node;
            try {
                node = currentUserSession.getNode(path);
                addSub(nodes, node);
            } catch (RepositoryException e) {
                logger.error(e.toString(), e);
                missedPaths.add(path + " could not be accessed : " + e.toString());
            }
        }
        for (JCRNodeWrapper node : nodes) {
            try {
                if (!node.hasPermission(Privilege.JCR_LOCK_MANAGEMENT)) {
                    missedPaths.add(node.getName() + ": write access denied");
                } else if (node.getLockedLocales().contains(currentUserSession.getLocale())
                        || (node.getLockedLocales().isEmpty() && node.isLocked())) {
                    if (!toLock) {
                        try {
                            node.unlock("user");
                        } catch (LockException e) {
                            logger.error(e.toString(), e);
                            missedPaths.add(node.getName() + ": repository exception");
                        }
                    } else {
                        String lockOwner = node.getLockOwner();
                        if (lockOwner != null && !lockOwner.equals(user.getName())) {
                            missedPaths.add(node.getName() + ": locked by " + lockOwner);
                        }
                    }
                } else {
                    if (toLock) {
                        if (!node.lockAndStoreToken("user")) {
                            missedPaths.add(node.getName() + ": repository exception");
                        }
                    } else if (node.isLocked()) {
                        node.unlock("user");
                    }
                }
            } catch (RepositoryException e) {
                logger.error(e.toString(), e);
                missedPaths.add(node.getName() + ": repository exception");
            }
        }
        try {
            currentUserSession.save();
        } catch (RepositoryException e) {
            logger.error(e.getMessage(), e);
            throw new GWTJahiaServiceException("Could not save session");
        }
        if (missedPaths.size() > 0) {
            StringBuilder errors = new StringBuilder("The following files could not be ")
                    .append(toLock ? "locked:" : "unlocked:");
            for (String missedPath : missedPaths) {
                errors.append("\n").append(missedPath);
            }
            throw new GWTJahiaServiceException(errors.toString());
        }
    }

    private void addSub(List<JCRNodeWrapper> nodes, JCRNodeWrapper node) throws RepositoryException {
        if (!nodes.contains(node)) {
            nodes.add(node);
        }
        NodeIterator ni = node.getNodes();
        while (ni.hasNext()) {
            JCRNodeWrapper subNode = (JCRNodeWrapper) ni.next();
            if (subNode.isNodeType("jnt:content")) {
                addSub(nodes, subNode);
            }
        }
    }

    /**
     * Upload file depending on operation (add version, auto-rename or just upload)
     *
     * @param location
     * @param tmpName
     * @param operation
     * @param newName
     * @param currentUserSession
     * @throws GWTJahiaServiceException
     */
    public void uploadedFile(String location, String tmpName, int operation, String newName,
            JCRSessionWrapper currentUserSession, Locale uiLocale, String httpSessionID)
            throws GWTJahiaServiceException {
        try {
            JCRNodeWrapper parent = currentUserSession.getNode(location);
            if (2 == operation) {
                JCRNodeWrapper node = parent.getNode(newName);
                if (node == null) {
                    throw new GWTJahiaServiceException(Messages.getInternalWithArguments(
                            "label.gwt.error.new.version.file.not.found", uiLocale, location, newName));
                }
                versioning.addNewVersionFile(node, tmpName, httpSessionID);
            } else {
                if (1 == operation) {
                    newName = findAvailableName(parent, newName);
                }
                if (parent.hasNode(newName)) {
                    throw new GWTJahiaServiceException(
                            Messages.getInternalWithArguments("label.gwt.error.file.exists", uiLocale, newName));
                }
                UploadedPendingFile item = fileStorage.getRequired(httpSessionID, tmpName);
                try {
                    InputStream is = null;
                    try {
                        is = item.getContentStream();
                        parent.uploadFile(newName, is, JCRContentUtils.getMimeType(newName, item.getContentType()));
                    } finally {
                        IOUtils.closeQuietly(is);
                    }
                } finally {
                    item.close();
                    fileStorage.remove(httpSessionID, tmpName);
                }
            }
            currentUserSession.save();
        } catch (RepositoryException e) {
            logger.error(e.getMessage(), e);
        }
    }

    public void markConflictAsResolved(String moduleId, GWTJahiaNode node, JCRSessionWrapper session)
            throws IOException, RepositoryException, BundleException {
        JahiaTemplatesPackage templatePackage = templateManagerService.getTemplatePackageById(moduleId);
        SourceControlManagement sourceControl = templatePackage.getSourceControl();
        if (sourceControl != null) {
            String path = node.getPath();
            path = path.substring(path.indexOf("/sources/") + "/sources".length());
            String sourcesFolderPath = templatePackage.getSourcesFolder().getAbsolutePath();
            sourceControl.markConflictAsResolved(new File(sourcesFolderPath + path));
        }
    }

    public GWTJahiaNode sendToSourceControl(String moduleId, String scmURI, String scmType,
            JCRSessionWrapper session) throws IOException, RepositoryException {
        templateManagerService.sendToSourceControl(moduleId, scmURI, scmType, session);
        return navigation.getGWTJahiaNode(session.getNode("/modules/" + moduleId),
                GWTJahiaNode.DEFAULT_SITE_FIELDS);
    }

    public List<GWTJahiaContentHistoryEntry> getContentHistory(JCRSessionWrapper session, String nodeIdentifier,
            int offset, int limit) throws RepositoryException {
        JCRNodeWrapper node = session.getNodeByIdentifier(nodeIdentifier);
        List<HistoryEntry> historyEntryList = contentHistoryService.getNodeHistory(node, true);
        List<GWTJahiaContentHistoryEntry> result = new ArrayList<GWTJahiaContentHistoryEntry>();
        for (HistoryEntry historyEntry : historyEntryList) {
            result.add(convertToGWTJahiaContentHistoryEntry(historyEntry));
        }
        Collections.sort(result, new Comparator<GWTJahiaContentHistoryEntry>() {
            public int compare(GWTJahiaContentHistoryEntry o1, GWTJahiaContentHistoryEntry o2) {
                return o2.compareTo(o1);
            }
        });

        return result;
    }

    private GWTJahiaContentHistoryEntry convertToGWTJahiaContentHistoryEntry(HistoryEntry historyEntry) {
        String languageCode = null;
        if (historyEntry.getLocale() != null) {
            languageCode = historyEntry.getLocale().toString();
        }
        return new GWTJahiaContentHistoryEntry(historyEntry.getDate() > 0 ? new Date(historyEntry.getDate()) : null,
                historyEntry.getAction(), historyEntry.getPropertyName(), historyEntry.getUserKey(),
                historyEntry.getPath(), historyEntry.getMessage(), languageCode);
    }

    public void deleteReferences(String path, JahiaUser user, JCRSessionWrapper currentUserSession, Locale uiLocale)
            throws GWTJahiaServiceException {
        List<String> missedPaths = new ArrayList<String>();
        JCRNodeWrapper referencedNode;
        try {
            referencedNode = currentUserSession.getNode(path);
            if (referencedNode != null) {
                if (!currentUserSession.getUserNode().isRoot() && referencedNode.isLocked()
                        && !referencedNode.getLockOwner().equals(user.getUsername())) {
                    missedPaths.add(referencedNode.getPath() + " - locked by " + referencedNode.getLockOwner());
                }
                if (!referencedNode.hasPermission(Privilege.JCR_REMOVE_NODE)) {
                    missedPaths.add(referencedNode.getPath() + " - ACCESS DENIED");
                } else if (!getRecursedLocksAndFileUsages(referencedNode, missedPaths, user.getUsername())) {
                    try {
                        String referenceToRemove = referencedNode.getIdentifier();
                        PropertyIterator r = referencedNode.getWeakReferences();
                        while (r.hasNext()) {
                            JCRPropertyWrapper reference = (JCRPropertyWrapper) r.next();
                            if (!reference.getPath().startsWith("/referencesKeeper")) {
                                if (!reference.getDefinition().isMandatory()) {
                                    if (reference.getDefinition().isMultiple()) {
                                        Value[] values = reference.getValues();
                                        if (values.length > 1) {
                                            Value[] valuesCopy = new Value[values.length - 1];
                                            int i = 0;
                                            for (Value value : values) {
                                                if (!value.getString().equals(referenceToRemove)) {
                                                    valuesCopy[i++] = value;
                                                }
                                            }
                                            reference.setValue(valuesCopy);
                                        } else {
                                            reference.remove();
                                        }
                                    } else {
                                        reference.remove();
                                    }
                                } else {
                                    missedPaths
                                            .add(referencedNode.getPath() + " - is use in a mandatory property of "
                                                    + reference.getParent().getPath());
                                }
                            }
                        }
                        currentUserSession.save();
                    } catch (AccessDeniedException e) {
                        missedPaths.add(referencedNode.getPath() + " - ACCESS DENIED");
                    } catch (ReferentialIntegrityException e) {
                        missedPaths.add(referencedNode.getPath() + " - is in use");
                    } catch (RepositoryException e) {
                        logger.error(e.getMessage(), e);
                        missedPaths.add(referencedNode.getPath() + " - UNSUPPORTED");
                    }
                }
            }
        } catch (RepositoryException e) {
            logger.error(e.getMessage(), e);
            missedPaths.add(
                    path + Messages.getInternal("label.gwt.error.could.not.be.accessed", uiLocale) + e.toString());
        }
        if (missedPaths.size() > 0) {
            StringBuilder errors = new StringBuilder(
                    Messages.getInternal("label.error.nodes.not.deleted", uiLocale));
            for (String err : missedPaths) {
                errors.append("\n").append(err);
            }
            throw new GWTJahiaServiceException(errors.toString());
        }
    }

    public void saveVisibilityConditions(GWTJahiaNode node, List<GWTJahiaNode> conditions,
            JCRSessionWrapper session, Locale uiLocale, String httpSessionID) throws GWTJahiaServiceException {
        try {
            JCRNodeWrapper parent = session.getNode(node.getPath());

            if (!conditions.isEmpty() && !parent.hasNode(VisibilityService.NODE_NAME)) {
                parent.addNode(VisibilityService.NODE_NAME, "jnt:conditionalVisibility");
            }

            String path = node.getPath() + "/" + VisibilityService.NODE_NAME;

            for (GWTJahiaNode condition : conditions) {
                List<GWTJahiaNodeProperty> props = condition.<List<GWTJahiaNodeProperty>>get("gwtproperties");
                if (condition.get("new-node") != null) {
                    GWTJahiaNode n = createNode(path, condition.getName(), condition.getNodeTypes().get(0),
                            new ArrayList<String>(), props, session, uiLocale, null, true, httpSessionID);
                    condition.setUUID(n.getUUID());
                    condition.setPath(n.getPath());
                } else {
                    JCRNodeWrapper jcrCondition = session.getNode(condition.getPath());
                    properties.setProperties(jcrCondition, props, httpSessionID);
                }
            }
            try {
                session.save();
            } catch (RepositoryException e) {
                logger.error(e.getMessage(), e);
                throw new GWTJahiaServiceException(
                        Messages.getInternalWithArguments("label.gwt.error.occur.when.trying.to.save.the.node",
                                uiLocale, node.getPath() + " (saveVisibilityConditions)"));
            }
            for (GWTJahiaNode condition : conditions) {
                if (condition.get("node-removed") != null) {
                    JCRNodeWrapper jcrCondition = session.getNode(condition.getPath());
                    jcrCondition.remove();
                }
            }

            if (parent.hasNode(VisibilityService.NODE_NAME)) {
                JCRNodeWrapper wrapper = parent.getNode(VisibilityService.NODE_NAME);
                if (node.get("node-visibility-forceMatchAllConditions") != null) {
                    wrapper.setProperty("j:forceMatchAllConditions",
                            (Boolean) node.get("node-visibility-forceMatchAllConditions"));
                }
                if (!wrapper.hasNodes()) {
                    wrapper.markForDeletion("");
                }
            }
            session.save();
        } catch (RepositoryException e) {
            logger.error(e.toString(), e);
            throw new GWTJahiaServiceException(e);
        }
    }
}