001/*
002 * (C) Copyright 2014 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-2.1.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 *     Vladimir Pasquier <vpasquier@nuxeo.com>
016 */
017package org.nuxeo.box.api.adapter;
018
019import org.nuxeo.box.api.marshalling.dao.BoxCollection;
020import org.nuxeo.box.api.marshalling.dao.BoxFile;
021import org.nuxeo.box.api.marshalling.dao.BoxFolder;
022import org.nuxeo.box.api.marshalling.dao.BoxItem;
023import org.nuxeo.box.api.marshalling.dao.BoxTypedObject;
024import org.nuxeo.box.api.marshalling.dao.BoxUser;
025import org.nuxeo.box.api.marshalling.exceptions.BoxJSONException;
026import org.nuxeo.box.api.service.BoxService;
027import org.joda.time.DateTime;
028import org.joda.time.format.ISODateTimeFormat;
029import org.nuxeo.ecm.core.api.CoreSession;
030import org.nuxeo.ecm.core.api.DocumentModel;
031import org.nuxeo.ecm.core.api.IdRef;
032import org.nuxeo.ecm.core.api.NuxeoPrincipal;
033import org.nuxeo.ecm.platform.tag.Tag;
034import org.nuxeo.ecm.platform.tag.TagService;
035import org.nuxeo.ecm.platform.usermanager.UserManager;
036import org.nuxeo.ecm.quota.size.QuotaAware;
037import org.nuxeo.ecm.quota.size.QuotaAwareDocument;
038import org.nuxeo.runtime.api.Framework;
039
040import java.lang.reflect.InvocationTargetException;
041import java.text.ParseException;
042import java.util.ArrayList;
043import java.util.Collections;
044import java.util.HashMap;
045import java.util.List;
046import java.util.Map;
047
048/**
049 * Abstract Box Adapter
050 *
051 * @since 5.9.2
052 */
053public abstract class BoxAdapter {
054
055    protected final DocumentModel doc;
056
057    protected final Map<String, Object> boxProperties = new HashMap<>();
058
059    protected BoxItem boxItem;
060
061    protected final BoxService boxService = Framework.getLocalService(BoxService.class);
062
063    public BoxAdapter(DocumentModel doc) {
064        this.doc = doc;
065        CoreSession session = doc.getCoreSession();
066
067        boxProperties.put(BoxItem.FIELD_ID, boxService.getBoxId(doc));
068        boxProperties.put(BoxItem.FIELD_SEQUENCE_ID, boxService.getBoxSequenceId(doc));
069        boxProperties.put(BoxItem.FIELD_ETAG, boxService.getBoxEtag(doc));
070
071        boxProperties.put(BoxItem.FIELD_NAME, doc.getName());
072        boxProperties.put(BoxItem.FIELD_CREATED_AT,
073                ISODateTimeFormat.dateTime().print(new DateTime(doc.getPropertyValue("dc:created"))));
074        boxProperties.put(BoxItem.FIELD_MODIFIED_AT,
075                ISODateTimeFormat.dateTime().print(new DateTime(doc.getPropertyValue("dc:modified"))));
076        boxProperties.put(BoxItem.FIELD_DESCRIPTION, doc.getPropertyValue("dc:description"));
077
078        // size
079        QuotaAwareDocument quotaAwareDocument = null;
080        if (Framework.getRuntime().getBundle("org.nuxeo.ecm.quota.core") != null) {
081            quotaAwareDocument = (QuotaAwareDocument) doc.getAdapter(QuotaAware.class);
082        }
083        boxProperties.put(BoxItem.FIELD_SIZE, quotaAwareDocument != null ? quotaAwareDocument.getInnerSize() : -1.0);
084
085        // path_collection
086        final DocumentModel parentDoc = session.getParentDocument(doc.getRef());
087        final Map<String, Object> pathCollection = new HashMap<>();
088        List<BoxTypedObject> hierarchy = getParentsHierarchy(session, parentDoc);
089        pathCollection.put(BoxCollection.FIELD_ENTRIES, hierarchy);
090        pathCollection.put(BoxCollection.FIELD_TOTAL_COUNT, hierarchy.size());
091        BoxCollection boxPathCollection = new BoxCollection(Collections.unmodifiableMap(pathCollection));
092        boxProperties.put(BoxItem.FIELD_PATH_COLLECTION, boxPathCollection);
093
094        // parent
095        final Map<String, Object> parentProperties = new HashMap<>();
096        parentProperties.put(BoxItem.FIELD_ID, boxService.getBoxId(parentDoc));
097        parentProperties.put(BoxItem.FIELD_SEQUENCE_ID, boxService.getBoxSequenceId(parentDoc));
098        parentProperties.put(BoxItem.FIELD_NAME, boxService.getBoxName(parentDoc));
099        parentProperties.put(BoxItem.FIELD_ETAG, boxService.getBoxEtag(parentDoc));
100        BoxFolder parentFolder = new BoxFolder(Collections.unmodifiableMap(parentProperties));
101        boxProperties.put(BoxItem.FIELD_PARENT, parentFolder);
102
103        // Users
104        // Creator
105        final UserManager userManager = Framework.getLocalService(UserManager.class);
106        String creator = doc.getPropertyValue("dc:creator") != null ? (String) doc.getPropertyValue("dc:creator")
107                : "system";
108        NuxeoPrincipal principalCreator = userManager.getPrincipal(creator);
109        final BoxUser boxCreator = boxService.fillUser(principalCreator);
110        boxProperties.put(BoxItem.FIELD_CREATED_BY, boxCreator);
111
112        // Last Contributor
113        String lastContributor = doc.getPropertyValue("dc:lastContributor") != null ? (String) doc.getPropertyValue("dc:lastContributor")
114                : "system";
115        final NuxeoPrincipal principalLastContributor = userManager.getPrincipal(lastContributor);
116        final BoxUser boxContributor = boxService.fillUser(principalLastContributor);
117        boxProperties.put(BoxItem.FIELD_MODIFIED_BY, boxContributor);
118
119        // Owner
120        boxProperties.put(BoxItem.FIELD_OWNED_BY, boxCreator);
121
122        // Shared Link
123        boxProperties.put(BoxItem.FIELD_SHARED_LINK, null);
124
125        // Status
126        boxProperties.put(BoxItem.FIELD_ITEM_STATUS, doc.getCurrentLifeCycleState());
127
128        // Tags
129        boxProperties.put(BoxItem.FIELD_TAGS, getTags(session));
130
131    }
132
133    public BoxItem getBoxItem() {
134        return boxItem;
135    }
136
137    abstract public BoxItem getMiniItem();
138
139    /**
140     * Update the box item properties
141     *
142     * @param boxItem containing values updated
143     */
144    public void setBoxItem(BoxItem boxItem) {
145        for (String field : boxItem.getKeySet()) {
146            this.boxItem.put(field, boxItem.getValue(field));
147        }
148    }
149
150    public DocumentModel getDoc() {
151        return doc;
152    }
153
154    protected List<BoxTypedObject> getParentsHierarchy(CoreSession session, DocumentModel parentDoc)
155            {
156        final List<BoxTypedObject> pathCollection = new ArrayList<>();
157        while (parentDoc != null) {
158            final Map<String, Object> parentCollectionProperties = new HashMap<>();
159            parentCollectionProperties.put(BoxItem.FIELD_ID, boxService.getBoxId(parentDoc));
160            parentCollectionProperties.put(BoxItem.FIELD_SEQUENCE_ID, boxService.getBoxSequenceId(parentDoc));
161            parentCollectionProperties.put(BoxItem.FIELD_ETAG, boxService.getBoxEtag(parentDoc));
162            parentCollectionProperties.put(BoxItem.FIELD_NAME, boxService.getBoxName(parentDoc));
163            BoxTypedObject boxParent;
164            // This different instantiation is related to the param type
165            // which is automatically added in json payload by Box marshaller
166            // following the box object type
167            if (parentDoc.isFolder()) {
168                boxParent = new BoxFolder(Collections.unmodifiableMap(parentCollectionProperties));
169            } else {
170                boxParent = new BoxFile(Collections.unmodifiableMap(parentCollectionProperties));
171            }
172            pathCollection.add(boxParent);
173            parentDoc = session.getParentDocument(parentDoc.getRef());
174        }
175        return pathCollection;
176    }
177
178    protected String[] getTags(CoreSession session) {
179        final TagService tagService = Framework.getLocalService(TagService.class);
180        final List<Tag> tags = tagService.getDocumentTags(session, doc.getId(), session.getPrincipal().getName());
181        final String[] tagNames = new String[tags.size()];
182        int index = 0;
183        for (Tag tag : tags) {
184            tagNames[index] = tag.getLabel();
185            index++;
186        }
187        return tagNames;
188    }
189
190    /**
191     * Update the document (nx/box sides) thanks to a box item
192     */
193    public void save(CoreSession session) throws ParseException, InvocationTargetException,
194            IllegalAccessException, BoxJSONException {
195
196        setDescription(boxItem.getDescription());
197        setCreator(boxItem.getOwnedBy().getId());
198
199        String id = boxItem.getParent().getId();
200        // check if id is root's one
201        String newParentId = "0".equals(id) ? session.getRootDocument().getId() : id;
202        IdRef documentIdRef = new IdRef(doc.getId());
203
204        // If the name has changed, update location in Nuxeo repository
205        // OR if parent id has been updated -> move the document
206        String oldParentId = session.getParentDocument(documentIdRef).getId();
207        if (!oldParentId.equals(newParentId) || !doc.getName().equals(boxItem.getName())) {
208
209            session.move(documentIdRef, new IdRef(newParentId), boxItem.getName());
210            // Title and name are same here
211            setTitle(boxItem.getName());
212        }
213
214        // Tags
215        TagService tagService = Framework.getLocalService(TagService.class);
216        if (tagService != null) {
217            if (boxItem.getTags().length != 0) {
218                tagService.removeTags(session, doc.getId());
219                for (String tag : boxItem.getTags()) {
220                    tagService.tag(session, doc.getId(), tag, session.getPrincipal().getName());
221                }
222            }
223        }
224        session.saveDocument(doc);
225        session.save();
226    }
227
228    public void setTitle(String value) {
229        doc.setPropertyValue("dc:title", value);
230    }
231
232    public void setDescription(String value) {
233        doc.setPropertyValue("dc:description", value);
234    }
235
236    public void setCreator(String value) {
237        doc.setPropertyValue("dc:creator", value);
238    }
239}