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.platform.filemanager.api.FileManager;
038import org.nuxeo.ecm.platform.query.api.PageProvider;
039import org.nuxeo.ecm.platform.query.api.PageProviderService;
040import org.nuxeo.runtime.api.Framework;
041
042/**
043 * {@link DocumentModel} backed implementation of a {@link FolderItem}.
044 *
045 * @author Antoine Taillefer
046 */
047public class DocumentBackedFolderItem extends AbstractDocumentBackedFileSystemItem implements FolderItem {
048
049    private static final long serialVersionUID = 1L;
050
051    private static final String FOLDER_ITEM_CHILDREN_PAGE_PROVIDER = "FOLDER_ITEM_CHILDREN";
052
053    protected boolean canCreateChild;
054
055    public DocumentBackedFolderItem(String factoryName, DocumentModel doc) {
056        this(factoryName, doc, false);
057    }
058
059    public DocumentBackedFolderItem(String factoryName, DocumentModel doc, boolean relaxSyncRootConstraint) {
060        super(factoryName, doc, relaxSyncRootConstraint);
061        initialize(doc);
062    }
063
064    public DocumentBackedFolderItem(String factoryName, FolderItem parentItem, DocumentModel doc) {
065        this(factoryName, parentItem, doc, false);
066    }
067
068    public DocumentBackedFolderItem(String factoryName, FolderItem parentItem, DocumentModel doc,
069            boolean relaxSyncRootConstraint) {
070        super(factoryName, parentItem, doc, relaxSyncRootConstraint);
071        initialize(doc);
072    }
073
074    protected DocumentBackedFolderItem() {
075        // Needed for JSON deserialization
076    }
077
078    /*--------------------- FileSystemItem ---------------------*/
079    @Override
080    public void rename(String name) {
081        try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
082            // Update doc properties
083            DocumentModel doc = getDocument(session);
084            doc.setPropertyValue("dc:title", name);
085            doc = session.saveDocument(doc);
086            session.save();
087            // Update FileSystemItem attributes
088            this.docTitle = name;
089            this.name = name;
090            updateLastModificationDate(doc);
091        }
092    }
093
094    /*--------------------- FolderItem -----------------*/
095    @Override
096    @SuppressWarnings("unchecked")
097    public List<FileSystemItem> getChildren() {
098        try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
099            PageProviderService pageProviderService = Framework.getLocalService(PageProviderService.class);
100            Map<String, Serializable> props = new HashMap<String, Serializable>();
101            props.put(CORE_SESSION_PROPERTY, (Serializable) session);
102            PageProvider<DocumentModel> childrenPageProvider = (PageProvider<DocumentModel>) pageProviderService.getPageProvider(
103                    FOLDER_ITEM_CHILDREN_PAGE_PROVIDER, null, null, 0L, props, docId);
104            Long pageSize = childrenPageProvider.getPageSize();
105
106            List<FileSystemItem> children = new ArrayList<FileSystemItem>();
107            int nbChildren = 0;
108            boolean reachedPageSize = false;
109            boolean hasNextPage = true;
110            // Since query results are filtered, make sure we iterate on PageProvider to get at most its page size
111            // number of
112            // FileSystemItems
113            while (nbChildren < pageSize && hasNextPage) {
114                List<DocumentModel> dmChildren = childrenPageProvider.getCurrentPage();
115                for (DocumentModel dmChild : dmChildren) {
116                    FileSystemItem child = getFileSystemItemAdapterService().getFileSystemItem(dmChild, this);
117                    if (child != null) {
118                        children.add(child);
119                        nbChildren++;
120                        if (nbChildren == pageSize) {
121                            reachedPageSize = true;
122                            break;
123                        }
124                    }
125                }
126                if (!reachedPageSize) {
127                    hasNextPage = childrenPageProvider.isNextPageAvailable();
128                    if (hasNextPage) {
129                        childrenPageProvider.nextPage();
130                    }
131                }
132            }
133
134            return children;
135        }
136    }
137
138    @Override
139    public boolean getCanCreateChild() {
140        return canCreateChild;
141    }
142
143    @Override
144    public FolderItem createFolder(String name) {
145        try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
146            DocumentModel folder = getFileManager().createFolder(session, name, docPath);
147            if (folder == null) {
148                throw new NuxeoException(
149                        String.format(
150                                "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.",
151                                name, docPath));
152            }
153            return (FolderItem) getFileSystemItemAdapterService().getFileSystemItem(folder, this);
154        } catch (NuxeoException e) {
155            e.addInfo(String.format("Error while trying to create folder %s as a child of doc %s", name, docPath));
156            throw e;
157        } catch (IOException e) {
158            throw new NuxeoException(String.format("Error while trying to create folder %s as a child of doc %s", name,
159                    docPath), e);
160        }
161    }
162
163    @Override
164    public FileItem createFile(Blob blob) {
165        String fileName = blob.getFilename();
166        try (CoreSession session = CoreInstance.openCoreSession(repositoryName, principal)) {
167            // TODO: manage conflict (overwrite should not necessarily be true)
168            DocumentModel file = getFileManager().createDocumentFromBlob(session, blob, docPath, true, fileName);
169            if (file == null) {
170                throw new NuxeoException(
171                        String.format(
172                                "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.",
173                                fileName, docPath));
174            }
175            return (FileItem) getFileSystemItemAdapterService().getFileSystemItem(file, this);
176        } catch (NuxeoException e) {
177            e.addInfo(String.format("Error while trying to create file %s as a child of doc %s", fileName, docPath));
178            throw e;
179        } catch (IOException e) {
180            throw new NuxeoException(String.format("Error while trying to create file %s as a child of doc %s",
181                    fileName, docPath), e);
182        }
183    }
184
185    /*--------------------- Protected -----------------*/
186    protected void initialize(DocumentModel doc) {
187        this.name = docTitle;
188        this.folder = true;
189        this.canCreateChild = doc.getCoreSession().hasPermission(doc.getRef(), SecurityConstants.ADD_CHILDREN);
190    }
191
192    protected FileManager getFileManager() {
193        return Framework.getLocalService(FileManager.class);
194    }
195
196    /*---------- Needed for JSON deserialization ----------*/
197    protected void setCanCreateChild(boolean canCreateChild) {
198        this.canCreateChild = canCreateChild;
199    }
200
201}