com.ikon.module.db.DbDocumentModule.java Source code

Java tutorial

Introduction

Here is the source code for com.ikon.module.db.DbDocumentModule.java

Source

/**
 * openkm, Open Document Management System (http://www.openkm.com)
 * Copyright (c) 2006-2013 Paco Avila & Josep Llort
 * 
 * No bytes were intentionally harmed during the development of this application.
 * 
 * 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 2 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, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package com.ikon.module.db;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Pattern;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;

import com.ikon.api.OKMDocument;
import com.ikon.automation.AutomationException;
import com.ikon.automation.AutomationManager;
import com.ikon.automation.AutomationUtils;
import com.ikon.bean.Document;
import com.ikon.bean.FileUploadResponse;
import com.ikon.bean.LockInfo;
import com.ikon.bean.Repository;
import com.ikon.bean.Version;
import com.ikon.cache.UserItemsManager;
import com.ikon.core.AccessDeniedException;
import com.ikon.core.Config;
import com.ikon.core.DatabaseException;
import com.ikon.core.FileSizeExceededException;
import com.ikon.core.ItemExistsException;
import com.ikon.core.LockException;
import com.ikon.core.MimeTypeConfig;
import com.ikon.core.PathNotFoundException;
import com.ikon.core.Ref;
import com.ikon.core.RepositoryException;
import com.ikon.core.UnsupportedMimeTypeException;
import com.ikon.core.UserQuotaExceededException;
import com.ikon.core.VersionException;
import com.ikon.core.VirusDetectedException;
import com.ikon.core.VirusDetection;
import com.ikon.dao.MimeTypeDAO;
import com.ikon.dao.NodeBaseDAO;
import com.ikon.dao.NodeDocumentDAO;
import com.ikon.dao.NodeDocumentVersionDAO;
import com.ikon.dao.NodeFolderDAO;
import com.ikon.dao.bean.AutomationRule;
import com.ikon.dao.bean.NodeBase;
import com.ikon.dao.bean.NodeDocument;
import com.ikon.dao.bean.NodeDocumentVersion;
import com.ikon.dao.bean.NodeFolder;
import com.ikon.dao.bean.NodeLock;
import com.ikon.extension.core.ExtensionException;
import com.ikon.module.DocumentModule;
import com.ikon.module.common.CommonGeneralModule;
import com.ikon.module.db.base.BaseDocumentModule;
import com.ikon.module.db.base.BaseModule;
import com.ikon.module.db.base.BaseNoteModule;
import com.ikon.module.db.base.BaseNotificationModule;
import com.ikon.principal.PrincipalAdapterException;
import com.ikon.spring.PrincipalUtils;
import com.ikon.util.ConfigUtils;
import com.ikon.util.FormatUtil;
import com.ikon.util.PathUtils;
import com.ikon.util.UserActivity;

public class DbDocumentModule implements DocumentModule {
    private static Logger log = LoggerFactory.getLogger(DbDocumentModule.class);

    @Override
    public Document create(String token, Document doc, InputStream is)
            throws UnsupportedMimeTypeException, FileSizeExceededException, UserQuotaExceededException,
            VirusDetectedException, ItemExistsException, PathNotFoundException, AccessDeniedException,
            RepositoryException, IOException, DatabaseException, ExtensionException, AutomationException {
        log.debug("create({}, {}, {})", new Object[] { token, doc, is });
        return create(token, doc, is, is.available(), null);
    }

    /**
     * Used when big files and WebDAV and GoogleDocs
     */
    public Document create(String token, Document doc, InputStream is, long size, String userId)
            throws UnsupportedMimeTypeException, FileSizeExceededException, UserQuotaExceededException,
            VirusDetectedException, ItemExistsException, PathNotFoundException, AccessDeniedException,
            RepositoryException, IOException, DatabaseException, ExtensionException, AutomationException {
        log.debug("create({}, {}, {}, {}, {})", new Object[] { token, doc, is, size, userId });
        return create(token, doc, is, size, userId, new Ref<FileUploadResponse>(null));
    }

    /**
     * Used when big files and FileUpload
     */
    public Document create(String token, Document doc, InputStream is, long size, String userId,
            Ref<FileUploadResponse> fuResponse)
            throws UnsupportedMimeTypeException, FileSizeExceededException, UserQuotaExceededException,
            VirusDetectedException, ItemExistsException, PathNotFoundException, AccessDeniedException,
            RepositoryException, IOException, DatabaseException, ExtensionException, AutomationException {
        log.debug("create({}, {}, {}, {}, {}, {})", new Object[] { token, doc, is, size, userId, fuResponse });
        Document newDocument = null;
        Authentication auth = null, oldAuth = null;

        if (Config.SYSTEM_READONLY) {
            throw new AccessDeniedException("System is in read-only mode");
        }

        String parentPath = PathUtils.getParent(doc.getPath());
        String name = PathUtils.getName(doc.getPath());

        // Add to KEA - must have the same extension
        int idx = name.lastIndexOf('.');
        String fileExtension = idx > 0 ? name.substring(idx) : ".tmp";
        File tmp = File.createTempFile("okm", fileExtension);

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            if (Config.MAX_FILE_SIZE > 0 && size > Config.MAX_FILE_SIZE) {
                log.error("Uploaded file size: {} ({}), Max file size: {} ({})",
                        new Object[] { FormatUtil.formatSize(size), size,
                                FormatUtil.formatSize(Config.MAX_FILE_SIZE), Config.MAX_FILE_SIZE });
                String usr = userId == null ? auth.getName() : userId;
                UserActivity.log(usr, "ERROR_FILE_SIZE_EXCEEDED", null, doc.getPath(), Long.toString(size));
                throw new FileSizeExceededException(Long.toString(size));
            }

            // Escape dangerous chars in name
            name = PathUtils.escape(name);

            if (!name.isEmpty()) {
                doc.setPath(parentPath + "/" + name);

                // Check file restrictions
                String mimeType = MimeTypeConfig.mimeTypes.getContentType(name.toLowerCase());
                doc.setMimeType(mimeType);

                if (Config.RESTRICT_FILE_MIME && MimeTypeDAO.findByName(mimeType) == null) {
                    String usr = userId == null ? auth.getName() : userId;
                    UserActivity.log(usr, "ERROR_UNSUPPORTED_MIME_TYPE", null, doc.getPath(), mimeType);
                    throw new UnsupportedMimeTypeException(mimeType);
                }

                // Restrict for extension
                if (!Config.RESTRICT_FILE_NAME.isEmpty()) {
                    StringTokenizer st = new StringTokenizer(Config.RESTRICT_FILE_NAME, Config.LIST_SEPARATOR);

                    while (st.hasMoreTokens()) {
                        String wc = st.nextToken().trim();
                        String re = ConfigUtils.wildcard2regexp(wc);

                        if (Pattern.matches(re, name)) {
                            String usr = userId == null ? auth.getName() : userId;
                            UserActivity.log(usr, "ERROR_UNSUPPORTED_MIME_TYPE", null, doc.getPath(), mimeType);
                            throw new UnsupportedMimeTypeException(mimeType);
                        }
                    }
                }

                // Manage temporary files
                byte[] buff = new byte[4 * 1024];
                FileOutputStream fos = new FileOutputStream(tmp);
                int read;

                while ((read = is.read(buff)) != -1) {
                    fos.write(buff, 0, read);
                }

                fos.flush();
                fos.close();
                is.close();
                is = new FileInputStream(tmp);

                if (!Config.SYSTEM_ANTIVIR.equals("")) {
                    String info = VirusDetection.detect(tmp);

                    if (info != null) {
                        String usr = userId == null ? auth.getName() : userId;
                        UserActivity.log(usr, "ERROR_VIRUS_DETECTED", null, doc.getPath(), info);
                        throw new VirusDetectedException(info);
                    }
                }

                String parentUuid = NodeBaseDAO.getInstance().getUuidFromPath(parentPath);
                NodeBase parentNode = NodeBaseDAO.getInstance().findByPk(parentUuid);

                // AUTOMATION - PRE
                // INSIDE BaseDocumentModule.create

                // Create node
                Set<String> keywords = doc.getKeywords() != null ? doc.getKeywords() : new HashSet<String>();
                NodeDocument docNode = BaseDocumentModule.create(auth.getName(), parentPath, parentNode, name,
                        doc.getTitle(), doc.getCreated(), mimeType, is, size, keywords, new HashSet<String>(),
                        fuResponse);

                // AUTOMATION - POST
                // INSIDE BaseDocumentModule.create

                // Set returned folder properties
                newDocument = BaseDocumentModule.getProperties(auth.getName(), docNode);

                // Setting wizard properties
                // INSIDE BaseDocumentModule.create

                if (fuResponse.get() == null) {
                    fuResponse.set(new FileUploadResponse());
                }

                fuResponse.get().setHasAutomation(AutomationManager.getInstance().hasAutomation());

                if (userId == null) {
                    // Check subscriptions
                    BaseNotificationModule.checkSubscriptions(docNode, auth.getName(), "CREATE_DOCUMENT", null);

                    // Check scripting
                    // BaseScriptingModule.checkScripts(session, parentNode, documentNode, "CREATE_DOCUMENT");

                    // Activity log
                    UserActivity.log(auth.getName(), "CREATE_DOCUMENT", docNode.getUuid(), doc.getPath(),
                            mimeType + ", " + size);
                } else {
                    // Check subscriptions
                    BaseNotificationModule.checkSubscriptions(docNode, userId, "CREATE_MAIL_ATTACHMENT", null);

                    // Check scripting
                    // BaseScriptingModule.checkScripts(session, parentNode, documentNode, "CREATE_MAIL_ATTACHMENT");

                    // Activity log
                    UserActivity.log(userId, "CREATE_MAIL_ATTACHMENT", docNode.getUuid(), doc.getPath(),
                            mimeType + ", " + size);
                }
            } else {
                throw new RepositoryException("Invalid document name");
            }
        } finally {
            IOUtils.closeQuietly(is);
            org.apache.commons.io.FileUtils.deleteQuietly(tmp);

            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("create: {}", newDocument);
        return newDocument;
    }

    @Override
    public void delete(String token, String docPath) throws LockException, PathNotFoundException,
            AccessDeniedException, RepositoryException, DatabaseException {
        log.debug("delete({}, {})", new Object[] { token, docPath });
        Authentication auth = null, oldAuth = null;

        if (Config.SYSTEM_READONLY) {
            throw new AccessDeniedException("System is in read-only mode");
        }

        if (OKMDocument.getInstance().isLocked(null, docPath)) {
            throw new AccessDeniedException("This document is locked, cannot delete.");
        }

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            String userTrashPath = "/" + Repository.TRASH + "/" + auth.getName();
            String userTrashUuid = NodeBaseDAO.getInstance().getUuidFromPath(userTrashPath);
            String name = PathUtils.getName(docPath);

            NodeDocumentDAO.getInstance().delete(name, docUuid, userTrashUuid, docPath);

            // Check subscriptions
            NodeDocument documentNode = NodeDocumentDAO.getInstance().findByPk(docUuid);
            BaseNotificationModule.checkSubscriptions(documentNode, PrincipalUtils.getUser(), "DELETE_DOCUMENT",
                    null);

            // Activity log
            UserActivity.log(auth.getName(), "DELETE_DOCUMENT", docUuid, docPath, null);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("delete: void");
    }

    @Override
    public Document rename(String token, String docPath, String newName) throws PathNotFoundException,
            ItemExistsException, AccessDeniedException, LockException, RepositoryException, DatabaseException {
        log.debug("rename({}, {}, {})", new Object[] { token, docPath, newName });
        Document renamedDocument = null;
        Authentication auth = null, oldAuth = null;

        if (Config.SYSTEM_READONLY) {
            throw new AccessDeniedException("System is in read-only mode");
        }

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String name = PathUtils.getName(docPath);
            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);

            // Escape dangerous chars in name
            newName = PathUtils.escape(newName);

            if (newName != null && !newName.isEmpty() && !newName.equals(name)) {
                NodeDocument documentNode = NodeDocumentDAO.getInstance().rename(docUuid, newName);
                renamedDocument = BaseDocumentModule.getProperties(auth.getName(), documentNode);

                // Check subscriptions
                BaseNotificationModule.checkSubscriptions(documentNode, PrincipalUtils.getUser(), "RENAME_DOCUMENT",
                        null);
            } else {
                // Don't change anything
                NodeDocument documentNode = NodeDocumentDAO.getInstance().findByPk(docUuid);
                renamedDocument = BaseDocumentModule.getProperties(auth.getName(), documentNode);
            }

            // Activity log
            UserActivity.log(auth.getName(), "RENAME_DOCUMENT", docUuid, docPath, newName);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("rename: {}", renamedDocument);
        return renamedDocument;
    }

    @Override
    public Document getProperties(String token, String docPath)
            throws PathNotFoundException, RepositoryException, DatabaseException {
        log.debug("getProperties({}, {})", token, docPath);
        Document doc = null;
        Authentication auth = null, oldAuth = null;

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            NodeDocument docNode = NodeDocumentDAO.getInstance().findByPk(docUuid);
            doc = BaseDocumentModule.getProperties(auth.getName(), docNode);

            // Activity log
            UserActivity.log(auth.getName(), "GET_DOCUMENT_PROPERTIES", docUuid, docPath, null);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("getProperties: {}", doc);
        return doc;
    }

    @Override
    public void setProperties(String token, Document doc) throws VersionException, LockException,
            PathNotFoundException, AccessDeniedException, RepositoryException, DatabaseException {
        log.debug("setProperties({}, {})", token, doc);
        Authentication auth = null, oldAuth = null;

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(doc.getPath());
            NodeDocument docNode = NodeDocumentDAO.getInstance().findByPk(docUuid);

            // Check subscriptions
            BaseNotificationModule.checkSubscriptions(docNode, auth.getName(), "SET_DOCUMENT_PROPERTIES", null);

            // Check scripting
            // BaseScriptingModule.checkScripts(session, documentNode, documentNode, "SET_DOCUMENT_PROPERTIES");

            // Activity log
            UserActivity.log(auth.getName(), "SET_DOCUMENT_PROPERTIES", docUuid, doc.getPath(), null);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("setProperties: void");
    }

    @Override
    public InputStream getContent(String token, String docPath, boolean checkout) throws PathNotFoundException,
            AccessDeniedException, RepositoryException, IOException, DatabaseException {
        log.debug("getContent({}, {}, {})", new Object[] { token, docPath, checkout });
        return getContent(token, docPath, checkout, true);
    }

    /**
     * Retrieve the content input stream from a document
     * 
     * @param token Authorization token.
     * @param docPath Path of the document to get the content.
     * @param checkout If the content is retrieved due to a checkout or not.
     * @param extendedSecurity If the extended security DOWNLOAD permission should be evaluated.
     * This is used to enable the document preview.
     */
    public InputStream getContent(String token, String docPath, boolean checkout, boolean extendedSecurity)
            throws PathNotFoundException, AccessDeniedException, RepositoryException, IOException,
            DatabaseException {
        log.debug("getContent({}, {}, {}, {})", new Object[] { token, docPath, checkout, extendedSecurity });
        InputStream is = null;
        Authentication auth = null, oldAuth = null;

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            is = BaseDocumentModule.getContent(auth.getName(), docPath, checkout, extendedSecurity);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("getContent: {}", is);
        return is;
    }

    @Override
    public InputStream getContentByVersion(String token, String docPath, String verName)
            throws RepositoryException, PathNotFoundException, IOException, DatabaseException {
        log.debug("getContentByVersion({}, {}, {})", new Object[] { token, docPath, verName });
        InputStream is = null;
        Authentication auth = null, oldAuth = null;

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            is = NodeDocumentVersionDAO.getInstance().getVersionContentByParent(docUuid, verName);

            // Activity log
            UserActivity.log(auth.getName(), "GET_DOCUMENT_CONTENT_BY_VERSION", docUuid, docPath,
                    verName + ", " + is.available());
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("getContentByVersion: {}", is);
        return is;
    }

    @Override
    @Deprecated
    public List<Document> getChilds(String token, String fldPath)
            throws PathNotFoundException, RepositoryException, DatabaseException {
        return getChildren(token, fldPath);
    }

    @Override
    public List<Document> getChildren(String token, String fldPath)
            throws PathNotFoundException, RepositoryException, DatabaseException {
        log.debug("getChildren({}, {})", token, fldPath);
        List<Document> children = new ArrayList<Document>();
        Authentication auth = null, oldAuth = null;

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String fldUuid = NodeBaseDAO.getInstance().getUuidFromPath(fldPath);

            for (NodeDocument nDocument : NodeDocumentDAO.getInstance().findByParent(fldUuid)) {
                children.add(BaseDocumentModule.getProperties(auth.getName(), nDocument));
            }

            // Activity log
            UserActivity.log(auth.getName(), "GET_CHILDREN_DOCUMENTS", fldUuid, fldPath, null);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("getChildren: {}", children);
        return children;
    }

    @Override
    public void checkout(String token, String docPath) throws LockException, PathNotFoundException,
            AccessDeniedException, RepositoryException, DatabaseException {
        checkout(token, docPath, null);
    }

    /**
     * Used in Zoho extension
     */
    public void checkout(String token, String docPath, String userId) throws LockException, PathNotFoundException,
            AccessDeniedException, RepositoryException, DatabaseException {
        log.debug("checkout({}, {}, {})", new Object[] { token, docPath, userId });
        Authentication auth = null, oldAuth = null;

        if (Config.SYSTEM_READONLY) {
            throw new AccessDeniedException("System is in read-only mode");
        }

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            if (userId == null) {
                userId = auth.getName();
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            NodeDocumentDAO.getInstance().checkout(userId, docUuid);

            // Activity log
            UserActivity.log(auth.getName(), "CHECKOUT_DOCUMENT", docUuid, docPath, null);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("checkout: void");
    }

    @Override
    public void cancelCheckout(String token, String docPath) throws LockException, PathNotFoundException,
            AccessDeniedException, RepositoryException, DatabaseException {
        log.debug("cancelCheckout({}, {})", token, docPath);
        cancelCheckoutHelper(token, docPath, false);
        log.debug("cancelCheckout: void");
    }

    @Override
    public void forceCancelCheckout(String token, String docPath) throws LockException, PathNotFoundException,
            AccessDeniedException, RepositoryException, DatabaseException, PrincipalAdapterException {
        log.debug("forceCancelCheckout({}, {})", token, docPath);

        if (PrincipalUtils.getRoles().contains(Config.DEFAULT_ADMIN_ROLE)) {
            cancelCheckoutHelper(token, docPath, true);
        } else {
            throw new AccessDeniedException("Only administrator use allowed");
        }

        log.debug("forceCancelCheckout: void");
    }

    /**
     * Implement cancelCheckout and forceCancelCheckout features
     */
    private void cancelCheckoutHelper(String token, String docPath, boolean force) throws LockException,
            PathNotFoundException, AccessDeniedException, RepositoryException, DatabaseException {
        log.debug("cancelCheckoutHelper({}, {}, {})", new Object[] { token, docPath, force });
        Authentication auth = null, oldAuth = null;
        String action = force ? "FORCE_CANCEL_DOCUMENT_CHECKOUT" : "CANCEL_DOCUMENT_CHECKOUT";

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            NodeDocument docNode = NodeDocumentDAO.getInstance().findByPk(docUuid);
            NodeDocumentDAO.getInstance().cancelCheckout(auth.getName(), docUuid, force);

            // Check subscriptions
            BaseNotificationModule.checkSubscriptions(docNode, auth.getName(), action, null);

            // Check scripting
            // BaseScriptingModule.checkScripts(session, documentNode, documentNode, action);

            // Activity log
            UserActivity.log(auth.getName(), action, docUuid, docPath, null);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("cancelCheckoutHelper: void");
    }

    @Override
    public boolean isCheckedOut(String token, String docPath)
            throws PathNotFoundException, RepositoryException, DatabaseException {
        log.debug("isCheckedOut({}, {})", token, docPath);
        boolean checkedOut = false;
        @SuppressWarnings("unused")
        Authentication auth = null, oldAuth = null;

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            checkedOut = NodeDocumentDAO.getInstance().isCheckedOut(docUuid);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("isCheckedOut: {}", checkedOut);
        return checkedOut;
    }

    @Override
    public Version checkin(String token, String docPath, InputStream is, String comment)
            throws FileSizeExceededException, UserQuotaExceededException, VirusDetectedException,
            AccessDeniedException, RepositoryException, PathNotFoundException, LockException, VersionException,
            IOException, DatabaseException {
        return checkin(token, docPath, is, comment, null);
    }

    /**
     * Used in Zoho extension
     */
    public Version checkin(String token, String docPath, InputStream is, String comment, String userId)
            throws FileSizeExceededException, UserQuotaExceededException, VirusDetectedException,
            AccessDeniedException, RepositoryException, PathNotFoundException, LockException, VersionException,
            IOException, DatabaseException {
        return checkin(token, docPath, is, is.available(), comment, userId);
    }

    /**
     * Used when big files and WebDAV
     */
    public Version checkin(String token, String docPath, InputStream is, long size, String comment, String userId)
            throws FileSizeExceededException, UserQuotaExceededException, VirusDetectedException,
            AccessDeniedException, RepositoryException, PathNotFoundException, LockException, VersionException,
            IOException, DatabaseException {
        log.debug("checkin({}, {}, {}, {}, {}, {})", new Object[] { token, docPath, is, size, comment, userId });
        Version version = new Version();
        Authentication auth = null, oldAuth = null;

        if (Config.SYSTEM_READONLY) {
            throw new AccessDeniedException("System is in read-only mode");
        }

        String name = PathUtils.getName(docPath);
        int idx = name.lastIndexOf('.');
        String fileExtension = idx > 0 ? name.substring(idx) : ".tmp";
        File tmp = File.createTempFile("okm", fileExtension);

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            if (userId == null) {
                userId = auth.getName();
            }

            if (Config.MAX_FILE_SIZE > 0 && size > Config.MAX_FILE_SIZE) {
                log.error("Uploaded file size: {} ({}), Max file size: {} ({})",
                        new Object[] { FormatUtil.formatSize(size), size,
                                FormatUtil.formatSize(Config.MAX_FILE_SIZE), Config.MAX_FILE_SIZE });
                UserActivity.log(userId, "ERROR_FILE_SIZE_EXCEEDED", null, docPath, Long.toString(size));
                throw new FileSizeExceededException(Long.toString(size));
            }

            // Manage temporary files
            byte[] buff = new byte[4 * 1024];
            FileOutputStream fos = new FileOutputStream(tmp);
            int read;

            while ((read = is.read(buff)) != -1) {
                fos.write(buff, 0, read);
            }

            fos.flush();
            fos.close();
            is.close();
            is = new FileInputStream(tmp);

            if (!Config.SYSTEM_ANTIVIR.equals("")) {
                String info = VirusDetection.detect(tmp);

                if (info != null) {
                    UserActivity.log(userId, "ERROR_VIRUS_DETECTED", null, docPath, info);
                    throw new VirusDetectedException(info);
                }
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            NodeDocument docNode = NodeDocumentDAO.getInstance().findByPk(docUuid);
            NodeDocumentVersion newDocVersion = NodeDocumentVersionDAO.getInstance().checkin(userId, comment,
                    docUuid, is, size);
            version = BaseModule.getProperties(newDocVersion);

            // Add comment (as system user)
            String text = "New version " + version.getName() + " by " + userId + ": " + comment;
            BaseNoteModule.create(docUuid, Config.SYSTEM_USER, text);

            // Update user items size
            if (Config.USER_ITEM_CACHE) {
                UserItemsManager.incSize(auth.getName(), size);
            }

            // Remove pdf & preview from cache
            CommonGeneralModule.cleanPreviewCache(docUuid);

            // Check subscriptions
            BaseNotificationModule.checkSubscriptions(docNode, userId, "CHECKIN_DOCUMENT", comment);

            // Check scripting
            // BaseScriptingModule.checkScripts(session, documentNode, documentNode, "CHECKIN_DOCUMENT");

            // Activity log
            UserActivity.log(auth.getName(), "CHECKIN_DOCUMENT", docUuid, docPath, size + ", " + comment);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            IOUtils.closeQuietly(is);
            org.apache.commons.io.FileUtils.deleteQuietly(tmp);

            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("checkin: {}", version);
        return version;
    }

    @Override
    public LockInfo lock(String token, String docPath) throws LockException, PathNotFoundException,
            AccessDeniedException, RepositoryException, DatabaseException {
        log.debug("lock({}, {})", token, docPath);
        LockInfo lck = null;
        Authentication auth = null, oldAuth = null;

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            NodeDocument docNode = NodeDocumentDAO.getInstance().findByPk(docUuid);
            NodeLock nLock = NodeDocumentDAO.getInstance().lock(auth.getName(), docUuid);
            lck = BaseModule.getProperties(nLock, docPath);

            // Check subscriptions
            BaseNotificationModule.checkSubscriptions(docNode, auth.getName(), "LOCK_DOCUMENT", null);

            // Check scripting
            // BaseScriptingModule.checkScripts(session, documentNode, documentNode, "LOCK_DOCUMENT");

            // Activity log
            UserActivity.log(auth.getName(), "LOCK_DOCUMENT", docUuid, docPath, lck.getToken());
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("lock: {}", lck);
        return lck;
    }

    @Override
    public void unlock(String token, String docPath) throws LockException, PathNotFoundException,
            AccessDeniedException, RepositoryException, DatabaseException {
        log.debug("unlock({}, {})", token, docPath);
        unlockHelper(token, docPath, false);
        log.debug("unlock: void");
    }

    @Override
    public void forceUnlock(String token, String docPath) throws LockException, PathNotFoundException,
            AccessDeniedException, RepositoryException, DatabaseException, PrincipalAdapterException {
        log.debug("forceUnlock({}, {})", token, docPath);

        if (PrincipalUtils.getRoles().contains(Config.DEFAULT_ADMIN_ROLE)) {
            unlockHelper(token, docPath, true);
        } else {
            throw new AccessDeniedException("Only administrator use allowed");
        }

        log.debug("forceUnlock: void");
    }

    /**
     * Implement unlock and forceUnlock features
     */
    private void unlockHelper(String token, String docPath, boolean force) throws LockException,
            PathNotFoundException, AccessDeniedException, RepositoryException, DatabaseException {
        log.debug("unlock({}, {}, {})", new Object[] { token, docPath, force });
        Authentication auth = null, oldAuth = null;
        String action = force ? "FORCE_UNLOCK_DOCUMENT" : "UNLOCK_DOCUMENT";

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            NodeDocument docNode = NodeDocumentDAO.getInstance().findByPk(docUuid);
            NodeDocumentDAO.getInstance().unlock(auth.getName(), docUuid, force);

            // Check subscriptions
            BaseNotificationModule.checkSubscriptions(docNode, auth.getName(), action, null);

            // Check scripting
            // BaseScriptingModule.checkScripts(session, documentNode, documentNode, action);

            // Activity log
            UserActivity.log(auth.getName(), action, docUuid, docPath, null);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("unlock: void");
    }

    @Override
    public boolean isLocked(String token, String docPath)
            throws RepositoryException, PathNotFoundException, DatabaseException {
        log.debug("isLocked({}, {})", token, docPath);
        boolean locked = false;
        @SuppressWarnings("unused")
        Authentication auth = null, oldAuth = null;

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            locked = NodeDocumentDAO.getInstance().isLocked(docUuid);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("isLocked: {}", locked);
        return locked;
    }

    @Override
    public LockInfo getLockInfo(String token, String docPath)
            throws RepositoryException, PathNotFoundException, LockException, DatabaseException {
        log.debug("getLock({}, {})", token, docPath);
        LockInfo lock = null;
        @SuppressWarnings("unused")
        Authentication auth = null, oldAuth = null;

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            NodeLock nLock = NodeDocumentDAO.getInstance().getLock(docUuid);
            lock = BaseModule.getProperties(nLock, docPath);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("getLock: {}", lock);
        return lock;
    }

    @Override
    public void purge(String token, String docPath) throws LockException, AccessDeniedException,
            RepositoryException, PathNotFoundException, DatabaseException {
        log.debug("purge({}, {})", token, docPath);
        @SuppressWarnings("unused")
        Authentication auth = null, oldAuth = null;

        if (Config.SYSTEM_READONLY) {
            throw new AccessDeniedException("System is in read-only mode");
        }

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            NodeDocumentDAO.getInstance().purge(docUuid);

            // Activity log - Already inside DAO
            // UserActivity.log(auth.getName(), "PURGE_DOCUMENT", docUuid, docPath, null);
        } catch (IOException e) {
            throw new RepositoryException(e.getMessage(), e);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("purge: void");
    }

    @Override
    public void move(String token, String docPath, String dstPath)
            throws PathNotFoundException, ItemExistsException, AccessDeniedException, LockException,
            RepositoryException, DatabaseException, ExtensionException, AutomationException {
        log.debug("move({}, {}, {})", new Object[] { token, docPath, dstPath });
        Authentication auth = null, oldAuth = null;

        if (Config.SYSTEM_READONLY) {
            throw new AccessDeniedException("System is in read-only mode");
        }

        if (OKMDocument.getInstance().isLocked(null, docPath)) {
            throw new AccessDeniedException("This document is locked, cannot move.");
        }

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            String dstUuid = NodeBaseDAO.getInstance().getUuidFromPath(dstPath);

            // AUTOMATION - PRE
            Map<String, Object> env = new HashMap<String, Object>();
            env.put(AutomationUtils.DOCUMENT_UUID, docUuid);
            env.put(AutomationUtils.FOLDER_UUID, dstUuid);
            AutomationManager.getInstance().fireEvent(AutomationRule.EVENT_DOCUMENT_MOVE, AutomationRule.AT_PRE,
                    env);

            NodeDocumentDAO.getInstance().move(docUuid, dstUuid);

            // AUTOMATION - POST
            AutomationManager.getInstance().fireEvent(AutomationRule.EVENT_DOCUMENT_MOVE, AutomationRule.AT_POST,
                    env);

            // Activity log
            UserActivity.log(auth.getName(), "MOVE_DOCUMENT", docUuid, docPath, dstPath);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("move: void");
    }

    @Override
    public void copy(String token, String docPath, String dstPath)
            throws ItemExistsException, PathNotFoundException, AccessDeniedException, RepositoryException,
            IOException, AutomationException, DatabaseException, UserQuotaExceededException {
        log.debug("copy({}, {}, {})", new Object[] { token, docPath, dstPath });
        Authentication auth = null, oldAuth = null;

        if (Config.SYSTEM_READONLY) {
            throw new AccessDeniedException("System is in read-only mode");
        }

        if (OKMDocument.getInstance().isLocked(null, docPath)) {
            throw new AccessDeniedException("This document is locked, cannot copy.");
        }

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            // Escape dangerous chars in name
            String docName = PathUtils.escape(PathUtils.getName(docPath));

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            String dstUuid = NodeBaseDAO.getInstance().getUuidFromPath(dstPath);
            NodeDocument srcDocNode = NodeDocumentDAO.getInstance().findByPk(docUuid);
            NodeFolder dstFldNode = NodeFolderDAO.getInstance().findByPk(dstUuid);
            NodeDocument newDocNode = BaseDocumentModule.copy(auth.getName(), srcDocNode, dstPath, dstFldNode,
                    docName);

            // Check subscriptions
            BaseNotificationModule.checkSubscriptions(dstFldNode, auth.getName(), "COPY_DOCUMENT", null);

            // Activity log
            UserActivity.log(auth.getName(), "COPY_DOCUMENT", newDocNode.getUuid(), docPath, dstPath);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }
    }

    @Override
    public void restoreVersion(String token, String docPath, String versionId) throws PathNotFoundException,
            AccessDeniedException, LockException, RepositoryException, DatabaseException {
        log.debug("restoreVersion({}, {}, {})", new Object[] { token, docPath, versionId });
        Authentication auth = null, oldAuth = null;

        if (Config.SYSTEM_READONLY) {
            throw new AccessDeniedException("System is in read-only mode");
        }

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            NodeDocumentVersionDAO.getInstance().restoreVersion(docUuid, versionId);

            // Remove pdf & preview from cache
            CommonGeneralModule.cleanPreviewCache(docUuid);

            // Activity log
            UserActivity.log(auth.getName(), "RESTORE_DOCUMENT_VERSION", docUuid, docPath, versionId);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("restoreVersion: void");
    }

    @Override
    public void purgeVersionHistory(String token, String docPath) throws AccessDeniedException,
            PathNotFoundException, LockException, RepositoryException, DatabaseException {
        log.debug("purgeVersionHistory({}, {})", token, docPath);
        Authentication auth = null, oldAuth = null;

        if (Config.SYSTEM_READONLY) {
            throw new AccessDeniedException("System is in read-only mode");
        }

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            NodeDocumentVersionDAO.getInstance().purgeVersionHistory(docUuid);

            // Activity log
            UserActivity.log(auth.getName(), "PURGE_DOCUMENT_VERSION_HISTORY", docUuid, docPath, null);
        } catch (IOException e) {
            throw new RepositoryException(e.getMessage(), e);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("purgeVersionHistory: void");
    }

    @Override
    public List<Version> getVersionHistory(String token, String docPath)
            throws PathNotFoundException, RepositoryException, DatabaseException {
        log.debug("getVersionHistory({}, {})", token, docPath);
        List<Version> history = new ArrayList<Version>();
        Authentication auth = null, oldAuth = null;

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            List<NodeDocumentVersion> docVersions = NodeDocumentVersionDAO.getInstance().findByParent(docUuid);

            for (NodeDocumentVersion nDocVersion : docVersions) {
                history.add(BaseModule.getProperties(nDocVersion));
            }

            // Activity log
            UserActivity.log(auth.getName(), "GET_DOCUMENT_VERSION_HISTORY", docUuid, docPath, null);
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("getVersionHistory: {}", history);
        return history;
    }

    @Override
    public long getVersionHistorySize(String token, String docPath)
            throws RepositoryException, PathNotFoundException, DatabaseException {
        log.debug("getVersionHistorySize({}, {})", token, docPath);
        long versionHistorySize = 0;
        @SuppressWarnings("unused")
        Authentication auth = null, oldAuth = null;

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);
            List<NodeDocumentVersion> docVersions = NodeDocumentVersionDAO.getInstance().findByParent(docUuid);

            for (NodeDocumentVersion nDocVersion : docVersions) {
                versionHistorySize += nDocVersion.getSize();
            }
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("getVersionHistorySize: {}", versionHistorySize);
        return versionHistorySize;
    }

    @Override
    public boolean isValid(String token, String docPath)
            throws PathNotFoundException, AccessDeniedException, RepositoryException, DatabaseException {
        log.debug("isValid({}, {})", token, docPath);
        boolean valid = true;
        @SuppressWarnings("unused")
        Authentication auth = null, oldAuth = null;

        try {
            if (token == null) {
                auth = PrincipalUtils.getAuthentication();
            } else {
                oldAuth = PrincipalUtils.getAuthentication();
                auth = PrincipalUtils.getAuthenticationByToken(token);
            }

            String docUuid = NodeBaseDAO.getInstance().getUuidFromPath(docPath);

            try {
                NodeDocumentDAO.getInstance().findByPk(docUuid);
            } catch (PathNotFoundException e) {
                valid = false;
            }
        } catch (DatabaseException e) {
            throw e;
        } finally {
            if (token != null) {
                PrincipalUtils.setAuthentication(oldAuth);
            }
        }

        log.debug("isValid: {}", valid);
        return valid;
    }

    @Override
    public String getPath(String token, String uuid)
            throws AccessDeniedException, RepositoryException, DatabaseException {
        try {
            return NodeBaseDAO.getInstance().getPathFromUuid(uuid);
        } catch (PathNotFoundException e) {
            throw new RepositoryException(e.getMessage(), e);
        }
    }
}