001/*
002 * (C) Copyright 2012 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Antoine Taillefer <ataillefer@nuxeo.com>
016 */
017package org.nuxeo.drive.adapter.impl;
018
019import static org.nuxeo.ecm.platform.query.nxql.CoreQueryDocumentPageProvider.CORE_SESSION_PROPERTY;
020
021import java.io.IOException;
022import java.io.Serializable;
023import java.util.ArrayList;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027
028import org.nuxeo.drive.adapter.FileItem;
029import org.nuxeo.drive.adapter.FileSystemItem;
030import org.nuxeo.drive.adapter.FolderItem;
031import org.nuxeo.ecm.core.api.Blob;
032import org.nuxeo.ecm.core.api.CoreInstance;
033import org.nuxeo.ecm.core.api.CoreSession;
034import org.nuxeo.ecm.core.api.DocumentModel;
035import org.nuxeo.ecm.core.api.NuxeoException;
036import org.nuxeo.ecm.core.api.security.SecurityConstants;
037import org.nuxeo.ecm.core.schema.FacetNames;
038import org.nuxeo.ecm.platform.filemanager.api.FileManager;
039import org.nuxeo.ecm.platform.query.api.PageProvider;
040import org.nuxeo.ecm.platform.query.api.PageProviderService;
041import org.nuxeo.runtime.api.Framework;
042
043/**
044 * {@link DocumentModel} backed implementation of a {@link FolderItem}.
045 *
046 * @author Antoine Taillefer
047 */
048public class DocumentBackedFolderItem extends AbstractDocumentBackedFileSystemItem implements FolderItem {
049
050    private static final long serialVersionUID = 1L;
051
052    private static final String FOLDER_ITEM_CHILDREN_PAGE_PROVIDER = "FOLDER_ITEM_CHILDREN";
053
054    protected boolean canCreateChild;
055
056    public DocumentBackedFolderItem(String factoryName, DocumentModel doc) {
057        this(factoryName, doc, false);
058    }
059
060    public DocumentBackedFolderItem(String factoryName, DocumentModel doc, boolean relaxSyncRootConstraint) {
061        super(factoryName, doc, relaxSyncRootConstraint);
062        initialize(doc);
063    }
064
065    public DocumentBackedFolderItem(String factoryName, FolderItem parentItem, DocumentModel doc) {
066        this(factoryName, parentItem, doc, false);
067    }
068
069    public DocumentBackedFolderItem(String factoryName, FolderItem parentItem, DocumentModel doc,
070            boolean relaxSyncRootConstraint) {
071        super(factoryName, parentItem, doc, relaxSyncRootConstraint);
072        initialize(doc);
073    }
074
075    protected DocumentBackedFolderItem() {
076        // Needed for JSON deserialization
077    }
078
079    /*--------------------- FileSystemItem ---------------------*/
080    @Override
081    public void rename(String name) {
082        try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
083            // Update doc properties
084            DocumentModel doc = getDocument(session);
085            doc.setPropertyValue("dc:title", name);
086            doc = session.saveDocument(doc);
087            session.save();
088            // Update FileSystemItem attributes
089            this.docTitle = name;
090            this.name = name;
091            updateLastModificationDate(doc);
092        }
093    }
094
095    /*--------------------- FolderItem -----------------*/
096    @Override
097    @SuppressWarnings("unchecked")
098    public List<FileSystemItem> getChildren() {
099        try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
100            PageProviderService pageProviderService = Framework.getLocalService(PageProviderService.class);
101            Map<String, Serializable> props = new HashMap<String, Serializable>();
102            props.put(CORE_SESSION_PROPERTY, (Serializable) session);
103            PageProvider<DocumentModel> childrenPageProvider = (PageProvider<DocumentModel>) pageProviderService.getPageProvider(
104                    FOLDER_ITEM_CHILDREN_PAGE_PROVIDER, null, null, 0L, props, docId);
105            Long pageSize = childrenPageProvider.getPageSize();
106
107            List<FileSystemItem> children = new ArrayList<FileSystemItem>();
108            int nbChildren = 0;
109            boolean reachedPageSize = false;
110            boolean hasNextPage = true;
111            // Since query results are filtered, make sure we iterate on PageProvider to get at most its page size
112            // number of
113            // FileSystemItems
114            while (nbChildren < pageSize && hasNextPage) {
115                List<DocumentModel> dmChildren = childrenPageProvider.getCurrentPage();
116                for (DocumentModel dmChild : dmChildren) {
117                    FileSystemItem child = getFileSystemItemAdapterService().getFileSystemItem(dmChild, this);
118                    if (child != null) {
119                        children.add(child);
120                        nbChildren++;
121                        if (nbChildren == pageSize) {
122                            reachedPageSize = true;
123                            break;
124                        }
125                    }
126                }
127                if (!reachedPageSize) {
128                    hasNextPage = childrenPageProvider.isNextPageAvailable();
129                    if (hasNextPage) {
130                        childrenPageProvider.nextPage();
131                    }
132                }
133            }
134
135            return children;
136        }
137    }
138
139    @Override
140    public boolean getCanCreateChild() {
141        return canCreateChild;
142    }
143
144    @Override
145    public FolderItem createFolder(String name) {
146        try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
147            DocumentModel folder = getFileManager().createFolder(session, name, docPath);
148            if (folder == null) {
149                throw new NuxeoException(
150                        String.format(
151                                "Cannot create folder named '%s' as a child of doc %s. Probably because of the allowed sub-types for this doc type, please check them.",
152                                name, docPath));
153            }
154            return (FolderItem) getFileSystemItemAdapterService().getFileSystemItem(folder, this);
155        } catch (NuxeoException e) {
156            e.addInfo(String.format("Error while trying to create folder %s as a child of doc %s", name, docPath));
157            throw e;
158        } catch (IOException e) {
159            throw new NuxeoException(String.format("Error while trying to create folder %s as a child of doc %s", name,
160                    docPath), e);
161        }
162    }
163
164    @Override
165    public FileItem createFile(Blob blob) {
166        String fileName = blob.getFilename();
167        try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
168            // TODO: manage conflict (overwrite should not necessarily be true)
169            DocumentModel file = getFileManager().createDocumentFromBlob(session, blob, docPath, true, fileName);
170            if (file == null) {
171                throw new NuxeoException(
172                        String.format(
173                                "Cannot create file '%s' as a child of doc %s. Probably because there are no file importers registered, please check the contributions to the <extension target=\"org.nuxeo.ecm.platform.filemanager.service.FileManagerService\" point=\"plugins\"> extension point.",
174                                fileName, docPath));
175            }
176            return (FileItem) getFileSystemItemAdapterService().getFileSystemItem(file, this);
177        } catch (NuxeoException e) {
178            e.addInfo(String.format("Error while trying to create file %s as a child of doc %s", fileName, docPath));
179            throw e;
180        } catch (IOException e) {
181            throw new NuxeoException(String.format("Error while trying to create file %s as a child of doc %s",
182                    fileName, docPath), e);
183        }
184    }
185
186    /*--------------------- Protected -----------------*/
187    protected void initialize(DocumentModel doc) {
188        this.name = docTitle;
189        this.folder = true;
190        this.canCreateChild = !doc.hasFacet(FacetNames.PUBLISH_SPACE)
191                && doc.getCoreSession().hasPermission(doc.getRef(), SecurityConstants.ADD_CHILDREN);
192    }
193
194    protected FileManager getFileManager() {
195        return Framework.getLocalService(FileManager.class);
196    }
197
198    /*---------- Needed for JSON deserialization ----------*/
199    protected void setCanCreateChild(boolean canCreateChild) {
200        this.canCreateChild = canCreateChild;
201    }
202
203}