/******************************************************************************* * Copyright (c) 2013 aegif. * * This file is part of NemakiWare. * * NemakiWare 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. * * NemakiWare 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 NemakiWare. * If not, see <http://www.gnu.org/licenses/>. * * Contributors: * linzhixing(https://github.com/linzhixing) - initial API and implementation ******************************************************************************/ package jp.aegif.nemaki.businesslogic.impl; import java.io.InputStream; import java.io.SequenceInputStream; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import jp.aegif.nemaki.businesslogic.ContentService; import jp.aegif.nemaki.businesslogic.rendition.RenditionManager; import jp.aegif.nemaki.cmis.aspect.query.solr.SolrUtil; import jp.aegif.nemaki.cmis.aspect.type.TypeManager; import jp.aegif.nemaki.cmis.factory.info.RepositoryInfo; import jp.aegif.nemaki.cmis.factory.info.RepositoryInfoMap; import jp.aegif.nemaki.dao.ContentDaoService; import jp.aegif.nemaki.model.Ace; import jp.aegif.nemaki.model.Acl; import jp.aegif.nemaki.model.Archive; import jp.aegif.nemaki.model.Aspect; import jp.aegif.nemaki.model.AttachmentNode; import jp.aegif.nemaki.model.Change; import jp.aegif.nemaki.model.Content; import jp.aegif.nemaki.model.Document; import jp.aegif.nemaki.model.Folder; import jp.aegif.nemaki.model.Item; import jp.aegif.nemaki.model.NodeBase; import jp.aegif.nemaki.model.Policy; import jp.aegif.nemaki.model.Property; import jp.aegif.nemaki.model.Relationship; import jp.aegif.nemaki.model.Rendition; import jp.aegif.nemaki.model.VersionSeries; import jp.aegif.nemaki.util.DataUtil; import jp.aegif.nemaki.util.PropertyManager; import jp.aegif.nemaki.util.constant.CmisPermission; import jp.aegif.nemaki.util.constant.NodeType; import jp.aegif.nemaki.util.constant.PrincipalId; import jp.aegif.nemaki.util.constant.PropertyKey; import jp.aegif.nemaki.util.constant.RenditionKind; import org.apache.chemistry.opencmis.commons.PropertyIds; import org.apache.chemistry.opencmis.commons.data.ContentStream; import org.apache.chemistry.opencmis.commons.data.ExtensionsData; import org.apache.chemistry.opencmis.commons.data.Properties; import org.apache.chemistry.opencmis.commons.data.PropertyData; import org.apache.chemistry.opencmis.commons.definitions.DocumentTypeDefinition; import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition; import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition; import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; import org.apache.chemistry.opencmis.commons.enums.ChangeType; import org.apache.chemistry.opencmis.commons.enums.ContentStreamAllowed; import org.apache.chemistry.opencmis.commons.enums.RelationshipDirection; import org.apache.chemistry.opencmis.commons.enums.Updatability; import org.apache.chemistry.opencmis.commons.enums.VersioningState; import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl; import org.apache.chemistry.opencmis.commons.impl.jaxb.CmisException; import org.apache.chemistry.opencmis.commons.server.CallContext; import org.apache.chemistry.opencmis.commons.spi.Holder; import org.apache.chemistry.opencmis.server.impl.CallContextImpl; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Node Service implementation * * @author linzhixing * */ public class ContentServiceImpl implements ContentService { private RepositoryInfoMap repositoryInfoMap; private ContentDaoService contentDaoService; private TypeManager typeManager; private RenditionManager renditionManager; private PropertyManager propertyManager; private SolrUtil solrUtil; private static final Log log = LogFactory.getLog(ContentServiceImpl.class); private final static String PATH_SEPARATOR = "/"; // /////////////////////////////////////// // Content // /////////////////////////////////////// @Override public boolean isRoot(String repositoryId, Content content) { String rootId = repositoryInfoMap.get(repositoryId).getRootFolderId(); if (content.isFolder() && rootId.equals(content.getId())) { return true; } else { return false; } } @Override public boolean isTopLevel(String repositoryId, Content content){ String rootId = repositoryInfoMap.get(repositoryId).getRootFolderId(); String parentId = content.getParentId(); return rootId.equals(parentId); } @Override public boolean existContent(String repositoryId, String objectTypeId) { return contentDaoService.existContent(repositoryId, objectTypeId); } @Override public Content getContent(String repositoryId, String objectId) { Content content = contentDaoService.getContent(repositoryId, objectId); if (content == null) return null; if (content.isDocument()) { return contentDaoService.getDocument(repositoryId, content.getId()); } else if (content.isFolder()) { return contentDaoService.getFolder(repositoryId, content.getId()); } else if (content.isRelationship()) { return contentDaoService.getRelationship(repositoryId, content.getId()); } else if (content.isPolicy()) { return contentDaoService.getPolicy(repositoryId, content.getId()); } else if (content.isItem()) { return contentDaoService.getItem(repositoryId, content.getId()); } else { return null; } } /** * Get the pieces of content available at that path. * * @throws CmisException */ @Override public Content getContentByPath(String repositoryId, String path) { List<String> splittedPath = splitLeafPathSegment(path); String rootId = repositoryInfoMap.get(repositoryId).getRootFolderId(); if (splittedPath.size() <= 0) { return null; } else if (splittedPath.size() == 1) { if (!splittedPath.get(0).equals(PATH_SEPARATOR)) return null; // root return contentDaoService.getFolder(repositoryId, rootId); } else { Content content = contentDaoService.getFolder(repositoryId, rootId); // Get the the leaf node for (int i = 1; i < splittedPath.size(); i++) { String nodeName = splittedPath.get(i); if (content == null) { log.warn("node '" + nodeName + "' in path '" + path + "' is not found."); return null; } else { Content child = contentDaoService.getChildByName(repositoryId, content.getId(), nodeName); content = child; } } //return if(content == null){ return null; }else{ return getContent(repositoryId, content.getId()); } } } private List<String> splitLeafPathSegment(String path) { List<String> splitted = new LinkedList<String>(); if (path.equals(PATH_SEPARATOR)) { splitted.add(PATH_SEPARATOR); return splitted; } // TODO validation for irregular path splitted = new LinkedList<String>(Arrays.asList(path.split(PATH_SEPARATOR))); splitted.remove(0); splitted.add(0, PATH_SEPARATOR); return splitted; } @Override public Folder getParent(String repositoryId, String objectId) { Content content = contentDaoService.getContent(repositoryId, objectId); return getFolder(repositoryId, content.getParentId()); } /** * Get children contents in a given folder */ @Override public List<Content> getChildren(String repositoryId, String folderId) { List<Content> children = new ArrayList<Content>(); List<Content> indices = contentDaoService.getChildren(repositoryId, folderId); if (CollectionUtils.isEmpty(indices)) return null; //TODO getを重複して行う必要なし for (Content c : indices) { if (c.isDocument()) { Document d = contentDaoService.getDocument(repositoryId, c.getId()); children.add(d); } else if (c.isFolder()) { Folder f = contentDaoService.getFolder(repositoryId, c.getId()); children.add(f); } else if (c.isPolicy()) { Policy p = contentDaoService.getPolicy(repositoryId, c.getId()); children.add(p); } else if (c.isItem()) { Item i = contentDaoService.getItem(repositoryId, c.getId()); children.add(i); } } return children; } @Override public Document getDocument(String repositoryId, String objectId) { return contentDaoService.getDocument(repositoryId, objectId); } @Override public Document getDocumentOfLatestVersion(String repositoryId, String versionSeriesId) { return contentDaoService.getDocumentOfLatestVersion(repositoryId, versionSeriesId); } @Override public Document getDocumentOfLatestMajorVersion(String repositoryId, String versionSeriesId) { return contentDaoService.getDocumentOfLatestMajorVersion(repositoryId, versionSeriesId); } @Override public List<Document> getAllVersions(CallContext callContext, String repositoryId, String versionSeriesId) { List<Document> results = new ArrayList<Document>(); // TODO hide PWC from a non-owner user List<Document> versions = contentDaoService.getAllVersions(repositoryId, versionSeriesId); if (CollectionUtils.isNotEmpty(versions)) { for (Document doc : versions) { if (!doc.isPrivateWorkingCopy()) { results.add(doc); } } } return contentDaoService.getAllVersions(repositoryId, versionSeriesId); } // TODO enable orderBy @Override public List<Document> getCheckedOutDocs(String repositoryId, String folderId, String orderBy, ExtensionsData extension) { return contentDaoService.getCheckedOutDocuments(repositoryId, folderId); } @Override public VersionSeries getVersionSeries(String repositoryId, Document document) { return getVersionSeries(repositoryId, document.getVersionSeriesId()); } @Override public VersionSeries getVersionSeries(String repositoryId, String versionSeriesId) { return contentDaoService.getVersionSeries(repositoryId, versionSeriesId); } @Override public Folder getFolder(String repositoryId, String objectId) { return contentDaoService.getFolder(repositoryId, objectId); } @Override public String calculatePath(String repositoryId, Content content) { List<String> path = calculatePathInternal(new ArrayList<String>(), content, repositoryId); path.remove(0); return PATH_SEPARATOR + StringUtils.join(path, PATH_SEPARATOR); } private List<String> calculatePathInternal(List<String> path, Content content, String repositoryId) { path.add(0, content.getName()); if (isRoot(repositoryId, content)) { return path; } else { Content parent = getParent(repositoryId, content.getId()); calculatePathInternal(path, parent, repositoryId); } return path; } @Override public Relationship getRelationship(String repositoryId, String objectId) { return contentDaoService.getRelationship(repositoryId, objectId); } @SuppressWarnings("unchecked") @Override public List<Relationship> getRelationsipsOfObject(String repositoryId, String objectId, RelationshipDirection relationshipDirection) { // Set default (according to the specification) relationshipDirection = (relationshipDirection == null) ? RelationshipDirection.SOURCE : relationshipDirection; switch (relationshipDirection) { case SOURCE: return contentDaoService.getRelationshipsBySource(repositoryId, objectId); case TARGET: return contentDaoService.getRelationshipsByTarget(repositoryId, objectId); case EITHER: List<Relationship> sources = contentDaoService.getRelationshipsBySource(repositoryId, objectId); List<Relationship> targets = contentDaoService.getRelationshipsByTarget(repositoryId, objectId); return (List<Relationship>) CollectionUtils.disjunction(sources, targets); default: return null; } } @Override public Policy getPolicy(String repositoryId, String objectId) { return contentDaoService.getPolicy(repositoryId, objectId); } @Override public List<Policy> getAppliedPolicies(String repositoryId, String objectId, ExtensionsData extension) { return contentDaoService.getAppliedPolicies(repositoryId, objectId); } @Override public Item getItem(String repositoryId, String objectId) { return contentDaoService.getItem(repositoryId, objectId); } private String writeChangeEvent(CallContext callContext, String repositoryId, Content content, ChangeType changeType) { return writeChangeEvent(callContext,repositoryId,content, null, changeType ); } public String writeChangeEvent(CallContext callContext, String repositoryId, Content content, Acl acl, ChangeType changeType) { Change change = new Change(); change.setAcl(acl); change.setObjectId(content.getId()); change.setChangeType(changeType); switch (changeType) { case CREATED: change.setTime(content.getCreated()); break; case UPDATED: change.setTime(content.getModified()); break; case DELETED: change.setTime(content.getCreated()); break; case SECURITY: change.setTime(content.getModified()); break; default: break; } change.setType(NodeType.CHANGE.value()); change.setName(content.getName()); change.setBaseType(content.getType()); change.setObjectType(content.getObjectType()); change.setParentId(content.getParentId()); /* * //Policy List<String> policyIds = new ArrayList<String>(); * List<Policy> policies = getAppliedPolicies(repositoryId, * content.getId(), null); if (!CollectionUtils.isEmpty(policies)) { for * (Policy p : policies) { policyIds.add(p.getId()); } } * change.setPolicyIds(policyIds); */ if (content.isDocument()) { Document d = (Document) content; change.setVersionSeriesId(d.getVersionSeriesId()); change.setVersionLabel(d.getVersionLabel()); } setSignature(callContext, change); change.setToken(generateChangeToken(change)); // Create a new change event Change created = contentDaoService.create(repositoryId, change); // Update change token of the content content.setChangeToken(created.getId()); update(repositoryId, content); return change.getToken(); } private String generateChangeToken(NodeBase node) { return String.valueOf(node.getCreated().getTimeInMillis()); } // TODO Create a rendition @Override public Document createDocument(CallContext callContext, String repositoryId, Properties properties, Folder parentFolder, ContentStream contentStream, VersioningState versioningState, String versionSeriesId) { Document d = buildNewBasicDocument(callContext, repositoryId, properties, parentFolder); // Check contentStreamAllowed DocumentTypeDefinition tdf = (DocumentTypeDefinition) (typeManager.getTypeDefinition(repositoryId, d)); ContentStreamAllowed csa = tdf.getContentStreamAllowed(); if (csa == ContentStreamAllowed.REQUIRED || csa == ContentStreamAllowed.ALLOWED && contentStream != null) { // Prepare ContentStream(to read it twice) // Map<String,ContentStream> contentStreamMap = // copyContentStream(contentStream); // Create Attachment node String attachmentId = createAttachment(callContext, repositoryId, contentStream); d.setAttachmentNodeId(attachmentId); // Create Renditions if (isPreviewEnabled()) { try { AttachmentNode an = getAttachment(repositoryId, attachmentId); ContentStream previewCS = new ContentStreamImpl(contentStream.getFileName(), contentStream .getBigLength(), contentStream.getMimeType(), an.getInputStream()); // ContentStream previewCS = // contentStreamMap.get("preview"); if (renditionManager.checkConvertible(previewCS.getMimeType())) { createPreview(callContext, repositoryId, previewCS, d); } } catch (Exception ex) { // not stop follow sequence log.error(ex); } } } // Set version properties VersionSeries vs = setVersionProperties(callContext, repositoryId, versioningState, d); // Create Document document = contentDaoService.create(repositoryId, d); // Update versionSeriesId#versionSeriesCheckedOutId after creating a PWC if (versioningState == VersioningState.CHECKEDOUT) { updateVersionSeriesWithPwc(callContext, repositoryId, vs, document); } // Write change event writeChangeEvent(callContext, repositoryId, document, ChangeType.CREATED); // Call Solr indexing(optional) solrUtil.callSolrIndexing(repositoryId); return document; } @Override public Document createDocumentFromSource(CallContext callContext, String repositoryId, Properties properties, Folder target, Document original, VersioningState versioningState, List<String> policies, org.apache.chemistry.opencmis.commons.data.Acl addAces, org.apache.chemistry.opencmis.commons.data.Acl removeAces) { Document copy = buildCopyDocumentWithBasicProperties(callContext, original); String attachmentId = copyAttachment(callContext, repositoryId, original.getAttachmentNodeId()); copy.setAttachmentNodeId(attachmentId); setVersionProperties(callContext, repositoryId, versioningState, copy); copy.setParentId(target.getId()); // Set updated properties updateProperties(callContext, repositoryId, properties, copy); setSignature(callContext, copy); // Create Document result = contentDaoService.create(repositoryId, copy); // Update versionSeriesId#versionSeriesCheckedOutId after creating a PWC if (versioningState == VersioningState.CHECKEDOUT) { updateVersionSeriesWithPwc(callContext, repositoryId, getVersionSeries(repositoryId, result), result); } // Record the change event writeChangeEvent(callContext, repositoryId, result, ChangeType.CREATED); // Call Solr indexing(optional) solrUtil.callSolrIndexing(repositoryId); return result; } @Override public Document createDocumentWithNewStream(CallContext callContext, String repositoryId, Document original, ContentStream contentStream) { Document copy = buildCopyDocumentWithBasicProperties(callContext, original); // Attachment String attachmentId = createAttachment(callContext, repositoryId, contentStream); copy.setAttachmentNodeId(attachmentId); // Rendition if (isPreviewEnabled()) { AttachmentNode an = getAttachment(repositoryId, attachmentId); ContentStream previewCS = new ContentStreamImpl(contentStream.getFileName(), contentStream .getBigLength(), contentStream.getMimeType(), an.getInputStream()); if (renditionManager.checkConvertible(previewCS.getMimeType())) { createPreview(callContext, repositoryId, previewCS, copy); } } // Set other properties // TODO externalize versionigState updateVersionProperties(callContext, repositoryId, VersioningState.MINOR, copy, original); // Create Document result = contentDaoService.create(repositoryId, copy); // Record the change event writeChangeEvent(callContext, repositoryId, result, ChangeType.CREATED); // Call Solr indexing(optional) solrUtil.callSolrIndexing(repositoryId); return result; } public Document replacePwc(CallContext callContext, String repositoryId, Document originalPwc, ContentStream contentStream) { // Update attachment contentStream AttachmentNode an = contentDaoService.getAttachment(repositoryId, originalPwc.getAttachmentNodeId()); contentDaoService.updateAttachment(repositoryId, an, contentStream); // Update rendition contentStream if (isPreviewEnabled()) { ContentStream previewCS = new ContentStreamImpl(contentStream.getFileName(), contentStream .getBigLength(), contentStream.getMimeType(), an.getInputStream()); if (renditionManager.checkConvertible(previewCS.getMimeType())) { List<String> renditionIds = originalPwc.getRenditionIds(); if (CollectionUtils.isNotEmpty(renditionIds)) { List<String> removedRenditionIds = new ArrayList<String>(); // Create preview for (String renditionId : renditionIds) { Rendition rd = contentDaoService.getRendition(repositoryId, renditionId); if (RenditionKind.CMIS_PREVIEW.equals(rd.getKind())) { removedRenditionIds.add(renditionId); createPreview(callContext, repositoryId, previewCS, originalPwc); } } // Update reference to preview ID renditionIds.removeAll(removedRenditionIds); originalPwc.setRenditionIds(renditionIds); } } } // Modify signature of pwc setSignature(callContext, originalPwc); // Record the change event writeChangeEvent(callContext, repositoryId, originalPwc, ChangeType.UPDATED); // Call Solr indexing(optional) solrUtil.callSolrIndexing(repositoryId); return originalPwc; } @Override public Document checkOut(CallContext callContext, String repositoryId, String objectId, ExtensionsData extension) { Document latest = getDocument(repositoryId, objectId); Document pwc = buildCopyDocumentWithBasicProperties(callContext, latest); // Create PWC attachment String attachmentId = copyAttachment(callContext, repositoryId, latest.getAttachmentNodeId()); pwc.setAttachmentNodeId(attachmentId); // Create PWC renditions copyRenditions(callContext, repositoryId, latest.getRenditionIds()); // Set other properties updateVersionProperties(callContext, repositoryId, VersioningState.CHECKEDOUT, pwc, latest); // Create PWC itself Document result = contentDaoService.create(repositoryId, pwc); // Modify versionSeries updateVersionSeriesWithPwc(callContext, repositoryId, getVersionSeries(repositoryId, result), result); // Write change event writeChangeEvent(callContext, repositoryId, result, ChangeType.CREATED); // Call Solr indexing(optional) solrUtil.callSolrIndexing(repositoryId); return result; } @Override public void cancelCheckOut(CallContext callContext, String repositoryId, String objectId, ExtensionsData extension) { Document pwc = getDocument(repositoryId, objectId); writeChangeEvent(callContext, repositoryId, pwc, ChangeType.DELETED); // Delete attachment & document itself(without archiving) contentDaoService.delete(repositoryId, pwc.getAttachmentNodeId()); contentDaoService.delete(repositoryId, pwc.getId()); VersionSeries vs = getVersionSeries(repositoryId, pwc); // Reverse the effect of checkout setModifiedSignature(callContext, vs); vs.setVersionSeriesCheckedOut(false); vs.setVersionSeriesCheckedOutBy(""); vs.setVersionSeriesCheckedOutId(""); contentDaoService.update(repositoryId, vs); List<Document> versions = getAllVersions(callContext, repositoryId, vs.getId()); if(CollectionUtils.isNotEmpty(versions)){ //Collections.sort(versions, new VersionComparator()); for(Document version : versions){ contentDaoService.refreshCmisObjectData(repositoryId, version.getId()); } } // Call Solr indexing(optional) solrUtil.callSolrIndexing(repositoryId); } @Override public Document checkIn(CallContext callContext, String repositoryId, Holder<String> objectId, Boolean major, Properties properties, ContentStream contentStream, String checkinComment, List<String> policies, org.apache.chemistry.opencmis.commons.data.Acl addAces, org.apache.chemistry.opencmis.commons.data.Acl removeAces, ExtensionsData extension) { String id = objectId.getValue(); Document pwc = getDocument(repositoryId, id); Document checkedIn = buildCopyDocumentWithBasicProperties(callContext, pwc); Document latest = getDocumentOfLatestVersion(repositoryId, pwc.getVersionSeriesId()); // When PWCUpdatable is true if (contentStream == null) { checkedIn.setAttachmentNodeId(copyAttachment(callContext, repositoryId, pwc.getAttachmentNodeId())); // When PWCUpdatable is false } else { checkedIn.setAttachmentNodeId(createAttachment(callContext, repositoryId, contentStream)); } // Set updated properties // updateProperties(callContext, properties, checkedIn); modifyProperties(callContext, repositoryId, properties, checkedIn); setSignature(callContext, checkedIn); checkedIn.setCheckinComment(checkinComment); // Reverse the effect of checkedout cancelCheckOut(callContext, repositoryId, id, extension); // update version information VersioningState versioningState = (major) ? VersioningState.MAJOR : VersioningState.MINOR; updateVersionProperties(callContext, repositoryId, versioningState, checkedIn, latest); // TODO set policies & ACEs // Create Document result = contentDaoService.create(repositoryId, checkedIn); // Record the change event writeChangeEvent(callContext, repositoryId, result, ChangeType.CREATED); // Call Solr indexing(optional) solrUtil.callSolrIndexing(repositoryId); return result; } private Document buildNewBasicDocument(CallContext callContext, String repositoryId, Properties properties, Folder parentFolder) { Document d = new Document(); setBaseProperties(callContext, repositoryId, properties, d, parentFolder.getId()); d.setParentId(parentFolder.getId()); d.setImmutable(DataUtil.getBooleanProperty(properties, PropertyIds.IS_IMMUTABLE)); setSignature(callContext, d); // Acl /*d.setAclInherited(true); d.setAcl(new Acl());*/ setAclOnCreated(callContext, repositoryId, d); return d; } private void setAclOnCreated(CallContext callContext, String repositoryId, Content content){ Acl acl = new Acl(); if(isTopLevel(repositoryId, content)){ Ace ace = new Ace(); ace.setPrincipalId(callContext.getUsername()); ace.setPermissions(new ArrayList<String>( Arrays.asList(CmisPermission.ALL))); acl.setLocalAces(new ArrayList<Ace>( Arrays.asList(ace) )); } content.setAcl(acl); content.setAclInherited(getAclInheritedWithDefault(repositoryId, content)); } private Document buildCopyDocumentWithBasicProperties(CallContext callContext, Document original) { Document copy = new Document(); copy.setType(original.getType()); copy.setObjectType(original.getObjectType()); copy.setName(original.getName()); copy.setDescription(original.getDescription()); copy.setParentId(original.getParentId()); copy.setImmutable(original.isImmutable()); copy.setAclInherited(original.isAclInherited()); copy.setAcl(original.getAcl()); copy.setAspects(original.getAspects()); copy.setSecondaryIds(original.getSecondaryIds()); setSignature(callContext, copy); return copy; } private VersionSeries setVersionProperties(CallContext callContext, String repositoryId, VersioningState versioningState, Document d) { // Version properties // CASE:New VersionSeries VersionSeries vs; vs = createVersionSeries(callContext, repositoryId, versioningState); d.setVersionSeriesId(vs.getId()); switch (versioningState) { // TODO NONE is not allowed case CHECKEDOUT: d.setLatestVersion(false); d.setMajorVersion(false); d.setLatestMajorVersion(false); d.setPrivateWorkingCopy(true); break; case MAJOR: d.setLatestVersion(true); d.setMajorVersion(true); d.setLatestMajorVersion(true); d.setVersionLabel("1.0"); d.setPrivateWorkingCopy(false); break; case MINOR: d.setLatestVersion(true); d.setMajorVersion(false); d.setLatestMajorVersion(false); d.setVersionLabel("0.1"); d.setPrivateWorkingCopy(false); break; default: break; } return vs; } private void updateVersionProperties(CallContext callContext, String repositoryId, VersioningState versioningState, Document d, Document former) { d.setVersionSeriesId(former.getVersionSeriesId()); switch (versioningState) { case MAJOR: d.setLatestVersion(true); d.setMajorVersion(true); d.setLatestMajorVersion(true); d.setVersionLabel(increasedVersionLabel(former, versioningState)); d.setPrivateWorkingCopy(false); former.setLatestVersion(false); former.setLatestMajorVersion(false); contentDaoService.update(repositoryId, former); break; case MINOR: d.setLatestVersion(true); d.setMajorVersion(false); d.setLatestMajorVersion(false); d.setVersionLabel(increasedVersionLabel(former, versioningState)); d.setPrivateWorkingCopy(false); former.setLatestVersion(false); contentDaoService.update(repositoryId, former); break; case CHECKEDOUT: d.setLatestVersion(false); d.setMajorVersion(false); d.setLatestMajorVersion(false); d.setPrivateWorkingCopy(true); // former latestVersion/latestMajorVersion remains unchanged default: break; } } private VersionSeries createVersionSeries(CallContext callContext, String repositoryId, VersioningState versioningState) { VersionSeries vs = new VersionSeries(); vs.setVersionSeriesCheckedOut(false); setSignature(callContext, vs); VersionSeries versionSeries = contentDaoService.create(repositoryId, vs); return versionSeries; } /** * Update versionSeriesId#versionSeriesCheckedOutId after creating a PWC * * @param callContext * @param repositoryId * TODO * @param versionSeries * @param pwc */ private void updateVersionSeriesWithPwc(CallContext callContext, String repositoryId, VersionSeries versionSeries, Document pwc) { versionSeries.setVersionSeriesCheckedOut(true); versionSeries.setVersionSeriesCheckedOutId(pwc.getId()); versionSeries.setVersionSeriesCheckedOutBy(callContext.getUsername()); contentDaoService.update(repositoryId, versionSeries); } @Override public Folder createFolder(CallContext callContext, String repositoryId, Properties properties, Folder parentFolder) { Folder f = new Folder(); setBaseProperties(callContext, repositoryId, properties, f, parentFolder.getId()); f.setParentId(parentFolder.getId()); // Defaults to document / folder / item if not specified List<String> allowedTypes = DataUtil.getIdListProperty(properties, PropertyIds.ALLOWED_CHILD_OBJECT_TYPE_IDS); if (CollectionUtils.isEmpty(allowedTypes)) { List<String> l = new ArrayList<String>(); l.add(BaseTypeId.CMIS_FOLDER.value()); l.add(BaseTypeId.CMIS_DOCUMENT.value()); l.add(BaseTypeId.CMIS_ITEM.value()); f.setAllowedChildTypeIds(l); } else { f.setAllowedChildTypeIds(allowedTypes); } setSignature(callContext, f); //Acl /*Acl acl = new Acl(); if (isRoot(repositoryId, parentFolder)){ Ace ace = new Ace(); ace.setPrincipalId(callContext.getUsername()); ace.setPermissions(new ArrayList<String>( Arrays.asList(CmisPermission.ALL))); acl.setLocalAces(new ArrayList<Ace>( Arrays.asList(ace) )); //f.setAclInherited(false); }else{ f.setAclInherited(true); } f.setAcl(acl);*/ setAclOnCreated(callContext, repositoryId, f); // Create Folder folder = contentDaoService.create(repositoryId, f); // Record the change event writeChangeEvent(callContext, repositoryId, folder, ChangeType.CREATED); // Call Solr indexing(optional) solrUtil.callSolrIndexing(repositoryId); return folder; } @Override public Relationship createRelationship(CallContext callContext, String repositoryId, Properties properties, List<String> policies, org.apache.chemistry.opencmis.commons.data.Acl addAces, org.apache.chemistry.opencmis.commons.data.Acl removeAces, ExtensionsData extension) { Relationship rel = new Relationship(); setBaseProperties(callContext, repositoryId, properties, rel, null); rel.setSourceId(DataUtil.getIdProperty(properties, PropertyIds.SOURCE_ID)); rel.setTargetId(DataUtil.getIdProperty(properties, PropertyIds.TARGET_ID)); // Set ACL rel.setAclInherited(true); rel.setAcl(new Acl()); Relationship relationship = contentDaoService.create(repositoryId, rel); // Record the change event writeChangeEvent(callContext, repositoryId, relationship, ChangeType.CREATED); return relationship; } @Override public Policy createPolicy(CallContext callContext, String repositoryId, Properties properties, List<String> policies, org.apache.chemistry.opencmis.commons.data.Acl addAces, org.apache.chemistry.opencmis.commons.data.Acl removeAces, ExtensionsData extension) { Policy p = new Policy(); setBaseProperties(callContext, repositoryId, properties, p, null); p.setPolicyText(DataUtil.getStringProperty(properties, PropertyIds.POLICY_TEXT)); p.setAppliedIds(new ArrayList<String>()); // Set ACL p.setAclInherited(true); p.setAcl(new Acl()); Policy policy = contentDaoService.create(repositoryId, p); // Record the change event writeChangeEvent(callContext, repositoryId, policy, ChangeType.CREATED); return policy; } @Override public Item createItem(CallContext callContext, String repositoryId, Properties properties, String folderId, List<String> policies, org.apache.chemistry.opencmis.commons.data.Acl addAces, org.apache.chemistry.opencmis.commons.data.Acl removeAces, ExtensionsData extension) { Item i = new Item(); setBaseProperties(callContext, repositoryId, properties, i, null); String objectTypeId = DataUtil.getIdProperty(properties, PropertyIds.OBJECT_TYPE_ID); TypeDefinition tdf = typeManager.getTypeDefinition(repositoryId, objectTypeId); if (tdf.isFileable()) { i.setParentId(folderId); } // Set ACL i.setAclInherited(true); i.setAcl(new Acl()); Item item = contentDaoService.create(repositoryId, i); // Record the change event writeChangeEvent(callContext, repositoryId, item, ChangeType.CREATED); return item; } private void setBaseProperties(CallContext callContext, String repositoryId, Properties properties, Content content, String parentFolderId) { // Object Type String objectTypeId = DataUtil.getIdProperty(properties, PropertyIds.OBJECT_TYPE_ID); content.setObjectType(objectTypeId); // Base Type TypeDefinition typeDefinition = typeManager.getTypeDefinition(repositoryId, objectTypeId); BaseTypeId baseTypeId = typeDefinition.getBaseTypeId(); content.setType(baseTypeId.value()); // Name(Unique in a folder) String uniqueName = buildUniqueName(repositoryId, DataUtil .getStringProperty(properties, PropertyIds.NAME), parentFolderId, null); content.setName(uniqueName); // Description content.setDescription(DataUtil.getStringProperty(properties, PropertyIds.DESCRIPTION)); // Secondary Type IDs content.setSecondaryIds(DataUtil.getIdListProperty(properties, PropertyIds.SECONDARY_OBJECT_TYPE_IDS)); // Signature setSignature(callContext, content); } private String copyAttachment(CallContext callContext, String repositoryId, String attachmentId) { AttachmentNode original = getAttachment(repositoryId, attachmentId); ContentStream cs = new ContentStreamImpl(original.getName(), BigInteger.valueOf(original.getLength()), original .getMimeType(), original.getInputStream()); AttachmentNode copy = new AttachmentNode(); copy.setName(original.getName()); copy.setLength(original.getLength()); copy.setMimeType(original.getMimeType()); setSignature(callContext, copy); return contentDaoService.createAttachment(repositoryId, copy, cs); } private List<String> copyRenditions(CallContext callContext, String repositoryId, List<String> renditionIds) { if (CollectionUtils.isEmpty(renditionIds)) return null; List<String> list = new ArrayList<String>(); for (String renditionId : renditionIds) { Rendition original = getRendition(repositoryId, renditionId); ContentStream cs = new ContentStreamImpl("content", BigInteger.valueOf(original.getLength()), original .getMimetype(), original.getInputStream()); Rendition copy = new Rendition(); copy.setKind(original.getKind()); copy.setHeight(original.getHeight()); copy.setWidth(original.getWidth()); copy.setLength(original.getLength()); copy.setMimetype(original.getMimetype()); setSignature(callContext, copy); String createdId = contentDaoService.createRendition(repositoryId, copy, cs); list.add(createdId); } return list; } private Content modifyProperties(CallContext callContext, String repositoryId, Properties properties, Content content) { if (properties == null || MapUtils.isEmpty(properties.getProperties())) { return content; } // Primary org.apache.chemistry.opencmis.commons.definitions.TypeDefinition td = typeManager .getTypeDefinition(repositoryId, content.getObjectType()); for (PropertyData<?> p : properties.getPropertyList()) { org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition pd = td.getPropertyDefinitions() .get(p.getId()); if (pd == null) continue; // CASE: READ&WRITE(ANYTIME) if (pd.getUpdatability() == Updatability.READWRITE) { setUpdatePropertyValue(repositoryId, content, p, properties); } // CASE:WHEN CHECKED OUT if (pd.getUpdatability() == Updatability.WHENCHECKEDOUT && content.isDocument()) { Document d = (Document) content; if (d.isPrivateWorkingCopy()) { setUpdatePropertyValue(repositoryId, content, p, properties); } } } // TODO // Subtype specific List<Property> subTypeProperties = buildSubTypeProperties(repositoryId, properties, content); if (!CollectionUtils.isEmpty(subTypeProperties)) { content.setSubTypeProperties(subTypeProperties); } // Secondary List<Aspect> secondary = buildSecondaryTypes(repositoryId, properties, content); if (!CollectionUtils.isEmpty(secondary)) { content.setAspects(secondary); } // Set modified signature setModifiedSignature(callContext, content); return content; } private List<Property> buildSubTypeProperties(String repositoryId, Properties properties, Content content) { List<PropertyDefinition<?>> subTypePropertyDefinitions = typeManager .getSpecificPropertyDefinitions(content.getObjectType()); if (CollectionUtils.isEmpty(subTypePropertyDefinitions)) return (new ArrayList<Property>()); return injectPropertyValue(subTypePropertyDefinitions, properties, content); } private List<Aspect> buildSecondaryTypes(String repositoryId, Properties properties, Content content) { List<Aspect> aspects = new ArrayList<Aspect>(); PropertyData secondaryTypeIds = properties.getProperties().get(PropertyIds.SECONDARY_OBJECT_TYPE_IDS); List<String> ids = new ArrayList<String>(); if (secondaryTypeIds == null) { ids = getSecondaryTypeIds(content); } else { ids = secondaryTypeIds.getValues(); } for (String secondaryTypeId : ids) { org.apache.chemistry.opencmis.commons.definitions.TypeDefinition td = typeManager .getTypeDefinition(repositoryId, secondaryTypeId); Aspect aspect = new Aspect(); aspect.setName(secondaryTypeId); List<Property> props = injectPropertyValue(td.getPropertyDefinitions().values(), properties, content); aspect.setProperties(props); aspects.add(aspect); } return aspects; } private List<String> getSecondaryTypeIds(Content content) { List<String> result = new ArrayList<String>(); List<Aspect> aspects = content.getAspects(); if (CollectionUtils.isNotEmpty(aspects)) { for (Aspect aspect : aspects) { result.add(aspect.getName()); } } return result; } private List<Property> injectPropertyValue(Collection<PropertyDefinition<?>> propertyDefnitions, Properties properties, Content content) { List<Property> props = new ArrayList<Property>(); for (PropertyDefinition<?> pd : propertyDefnitions) { switch (pd.getUpdatability()) { case READONLY: continue; case READWRITE: break; case WHENCHECKEDOUT: if (!content.isDocument()) { continue; } else { Document d = (Document) content; if (!d.isPrivateWorkingCopy()) { continue; } } break; default: continue; } PropertyData<?> property = properties.getProperties().get(pd.getId()); if (property == null) continue; Property p = new Property(); p.setKey(property.getId()); switch (pd.getCardinality()) { case SINGLE: p.setValue(property.getFirstValue()); break; case MULTI: p.setValue(property.getValues()); break; default: break; } props.add(p); } return props; } @Override public Content updateProperties(CallContext callContext, String repositoryId, Properties properties, Content content) { Content modified = modifyProperties(callContext, repositoryId, properties, content); Content result = update(repositoryId, modified); // Record the change event writeChangeEvent(callContext, repositoryId, result, ChangeType.UPDATED); return result; } @Override public Content update(String repositoryId, Content content) { Content result = null; if (content instanceof Document) { result = contentDaoService.update(repositoryId, (Document) content); } else if (content instanceof Folder) { result = contentDaoService.update(repositoryId, (Folder) content); } else if (content instanceof Relationship) { result = contentDaoService.update(repositoryId, (Relationship) content); } else if (content instanceof Policy) { result = contentDaoService.update(repositoryId, (Policy) content); } else if (content instanceof Item) { result = contentDaoService.update(repositoryId, (Item) content); } // Call Solr indexing(optional) solrUtil.callSolrIndexing(repositoryId); return result; } // TODO updatable CMIS properties are hard-coded. private void setUpdatePropertyValue(String repositoryId, Content content, PropertyData<?> propertyData, Properties properties) { if (propertyData.getId().equals(PropertyIds.NAME)) { if (DataUtil.getIdProperty(properties, PropertyIds.OBJECT_ID) != content.getId()) { String uniqueName = buildUniqueName(repositoryId, DataUtil .getStringProperty(properties, PropertyIds.NAME), content.getParentId(), content); content.setName(uniqueName); } } if (propertyData.getId().equals(PropertyIds.DESCRIPTION)) { content.setDescription(DataUtil.getStringProperty(properties, propertyData.getId())); } if (propertyData.getId().equals(PropertyIds.SECONDARY_OBJECT_TYPE_IDS)) { content.setSecondaryIds(DataUtil.getIdListProperty(properties, PropertyIds.SECONDARY_OBJECT_TYPE_IDS)); } } @Override public void move(CallContext callContext, String repositoryId, Content content, Folder target) { String sourceId = content.getParentId(); content.setParentId(target.getId()); String uniqueName = buildUniqueName(repositoryId, content.getName(), target.getId(), null); content.setName(uniqueName); move(repositoryId, content, sourceId); Folder source = getFolder(repositoryId, sourceId); writeChangeEvent(callContext, repositoryId, source, ChangeType.UPDATED); writeChangeEvent(callContext, repositoryId, target, ChangeType.UPDATED); // Call Solr indexing(optional) solrUtil.callSolrIndexing(repositoryId); } private Content move(String repositoryId, Content content, String sourceId){ Content result = null; if(content instanceof Document){ result = contentDaoService.move(repositoryId, (Document)content, sourceId); }else if(content instanceof Folder){ result = contentDaoService.move(repositoryId, (Folder)content, sourceId); } return result; } @Override public void applyPolicy(CallContext callContext, String repositoryId, String policyId, String objectId, ExtensionsData extension) { Policy policy = getPolicy(repositoryId, policyId); List<String> ids = policy.getAppliedIds(); ids.add(objectId); policy.setAppliedIds(ids); contentDaoService.update(repositoryId, policy); // Record the change event Content content = getContent(repositoryId, objectId); writeChangeEvent(callContext, repositoryId, content, ChangeType.SECURITY); } @Override public void removePolicy(CallContext callContext, String repositoryId, String policyId, String objectId, ExtensionsData extension) { Policy policy = getPolicy(repositoryId, policyId); List<String> ids = policy.getAppliedIds(); ids.remove(objectId); policy.setAppliedIds(ids); contentDaoService.update(repositoryId, policy); // Record the change event Content content = getContent(repositoryId, objectId); writeChangeEvent(callContext, repositoryId, content, ChangeType.SECURITY); } /** * Delete a Content. */ @Override public void delete(CallContext callContext, String repositoryId, String objectId, Boolean deletedWithParent) { Content content = getContent(repositoryId, objectId); //TODO workaround if(content == null){ //If content is already deleted, do nothing; return; } // Record the change event(Before the content is deleted!) writeChangeEvent(callContext, repositoryId, content, ChangeType.DELETED); // Archive and then Delete createArchive(callContext, repositoryId, objectId, deletedWithParent); contentDaoService.delete(repositoryId, objectId); // Call Solr indexing(optional) solrUtil.callSolrIndexing(repositoryId); } @Override public void deleteAttachment(CallContext callContext, String repositoryId, String attachmentId) { createAttachmentArchive(callContext, repositoryId, attachmentId); contentDaoService.delete(repositoryId, attachmentId); } @Override public void deleteContentStream(CallContext callContext, String repositoryId, Holder<String> objectId) { // TODO Auto-generated method stub } @Override public void deleteDocument(CallContext callContext, String repositoryId, String objectId, Boolean allVersions, Boolean deleteWithParent) { Document document = (Document) getContent(repositoryId, objectId); // Make the list of objects to be deleted List<Document> versionList = new ArrayList<Document>(); String versionSeriesId = document.getVersionSeriesId(); if (allVersions) { versionList = getAllVersions(callContext, repositoryId, versionSeriesId); } else { versionList.add(document); } // Delete for (Document version : versionList) { // Archive a document if (version.getAttachmentNodeId() != null) { String attachmentId = version.getAttachmentNodeId(); // Delete an attachment deleteAttachment(callContext, repositoryId, attachmentId); } // Delete rendition(no need for archive) if (CollectionUtils.isNotEmpty(version.getRenditionIds())) { for (String renditionId : version.getRenditionIds()) { contentDaoService.delete(repositoryId, renditionId); } } // Delete a document delete(callContext, repositoryId, version.getId(), deleteWithParent); } // Move up the latest version if (!allVersions) { Document latestVersion = getDocumentOfLatestVersion(repositoryId, versionSeriesId); if (latestVersion != null) { latestVersion.setLatestVersion(true); latestVersion.setLatestMajorVersion(latestVersion.isMajorVersion()); contentDaoService.update(repositoryId, latestVersion); } } // Call Solr indexing(optional) solrUtil.callSolrIndexing(repositoryId); } // deletedWithParent flag controls whether it's deleted with the parent all // together. @Override public List<String> deleteTree(CallContext callContext, String repositoryId, String folderId, Boolean allVersions, Boolean continueOnFailure, Boolean deletedWithParent) { List<String> failureIds = new ArrayList<String>(); // Delete children List<Content> children = getChildren(repositoryId, folderId); if (!CollectionUtils.isEmpty(children)) { for (Content child : children) { try { if (child.isFolder()) { deleteTree(callContext, repositoryId, child.getId(), allVersions, continueOnFailure, true); } else if (child.isDocument()) { deleteDocument(callContext, repositoryId, child.getId(), allVersions, true); } else { delete(callContext, repositoryId, child.getId(), true); } } catch (Exception e) { if (continueOnFailure) { failureIds.add(child.getId()); continue; } else { log.error("", e); } } } } // Delete the folder itself try { delete(callContext, repositoryId, folderId, deletedWithParent); } catch (Exception e) { if (continueOnFailure) { failureIds.add(folderId); } else { log.error("", e); } } return failureIds; } @Override public AttachmentNode getAttachment(String repositoryId, String attachmentId) { AttachmentNode an = contentDaoService.getAttachment(repositoryId, attachmentId); contentDaoService.setStream(repositoryId, an); return an; } @Override public AttachmentNode getAttachmentRef(String repositoryId, String attachmentId) { AttachmentNode an = contentDaoService.getAttachment(repositoryId, attachmentId); return an; } private String createAttachment(CallContext callContext, String repositoryId, ContentStream contentStream) { AttachmentNode a = new AttachmentNode(); a.setMimeType(contentStream.getMimeType()); a.setLength(contentStream.getLength()); setSignature(callContext, a); return contentDaoService.createAttachment(repositoryId, a, contentStream); } private String createPreview(CallContext callContext, String repositoryId, ContentStream contentStream, Document document) { Rendition rendition = new Rendition(); rendition.setTitle("PDF Preview"); rendition.setKind(RenditionKind.CMIS_PREVIEW.value()); rendition.setMimetype(contentStream.getMimeType()); rendition.setLength(contentStream.getLength()); ContentStream converted = renditionManager.convertToPdf(contentStream, document.getName()); setSignature(callContext, rendition); if (converted == null) { // TODO logging return null; } else { String renditionId = contentDaoService.createRendition(repositoryId, rendition, converted); List<String> renditionIds = document.getRenditionIds(); if (renditionIds == null) { document.setRenditionIds(new ArrayList<String>()); } document.getRenditionIds().add(renditionId); return renditionId; } } private boolean isPreviewEnabled() { String _cpbltyPreview = propertyManager.readValue(PropertyKey.CAPABILITY_EXTENDED_PREVIEW); boolean cpbltyPreview = (Boolean.valueOf(_cpbltyPreview) == null) ? false : Boolean.valueOf(_cpbltyPreview); return cpbltyPreview; } @Override public void appendAttachment(CallContext callContext, String repositoryId, Holder<String> objectId, Holder<String> changeToken, ContentStream contentStream, boolean isLastChunk, ExtensionsData extension) { Document document = contentDaoService.getDocument(repositoryId, objectId.getValue()); AttachmentNode attachment = getAttachment(repositoryId, document.getAttachmentNodeId()); InputStream is = attachment.getInputStream(); // Append SequenceInputStream sis = new SequenceInputStream(is, contentStream.getStream()); // appendStream will be used for a huge file, so avoid reading stream long length = attachment.getLength() + contentStream.getLength(); ContentStream cs = new ContentStreamImpl("content", BigInteger.valueOf(length), attachment.getMimeType(), sis); contentDaoService.updateAttachment(repositoryId, attachment, cs); writeChangeEvent(callContext, repositoryId, document, ChangeType.UPDATED); } @Override public Rendition getRendition(String repositoryId, String streamId) { return contentDaoService.getRendition(repositoryId, streamId); } @Override public List<Rendition> getRenditions(String repositoryId, String objectId) { Content c = getContent(repositoryId, objectId); List<String> ids = new ArrayList<String>(); if (c.isDocument()) { Document d = (Document) c; ids = d.getRenditionIds(); } else if (c.isFolder()) { Folder f = (Folder) c; ids = f.getRenditionIds(); } else { return null; } List<Rendition> renditions = new ArrayList<Rendition>(); if (CollectionUtils.isNotEmpty(ids)) { for (String id : ids) { renditions.add(contentDaoService.getRendition(repositoryId, id)); } } return renditions; } // /////////////////////////////////////// // Acl // /////////////////////////////////////// // Merge inherited ACL @Override public Acl calculateAcl(String repositoryId, Content content) { Acl acl = content.getAcl(); //boolean iht = (content.isAclInherited() == null) ? false : content.isAclInherited(); boolean iht = getAclInheritedWithDefault(repositoryId, content); if (!isRoot(repositoryId, content) && iht) { // Caching the results of calculation List<Ace> aces = new ArrayList<Ace>(); List<Ace> result = calculateAclInternal(repositoryId, aces, content); // Convert result to Acl Acl _acl = new Acl(); for (Ace r : result) { if (r.isDirect()) { _acl.getLocalAces().add(r); } else { _acl.getInheritedAces().add(r); } } acl = _acl; } // Convert anonymous and anyone convertSystemPrincipalId(repositoryId, acl.getAllAces()); return acl; } private List<Ace> calculateAclInternal(String repositoryId, List<Ace> result, Content content) { if (isRoot(repositoryId, content) || !getAclInheritedWithDefault(repositoryId, content)) { List<Ace> rootAces = new ArrayList<Ace>(); List<Ace> aces = content.getAcl().getLocalAces(); for (Ace ace : aces) { Ace rootAce = deepCopy(ace); rootAce.setDirect(true); rootAces.add(rootAce); } return mergeAcl(repositoryId, result, rootAces); } else { Content parent = getParent(repositoryId, content.getId()); return mergeAcl(repositoryId, content.getAcl() .getLocalAces(), calculateAclInternal(repositoryId, new ArrayList<Ace>(), parent)); } } private List<Ace> mergeAcl(String repositoryId, List<Ace> target, List<Ace> source) { HashMap<String, Ace> _result = new HashMap<String, Ace>(); // convert Normalize system principal id token to a real one convertSystemPrincipalId(repositoryId, target); HashMap<String, Ace> targetMap = buildAceMap(target); HashMap<String, Ace> sourceMap = buildAceMap(source); for (Entry<String, Ace> t : targetMap.entrySet()) { Ace ace = deepCopy(t.getValue()); ace.setDirect(true); _result.put(t.getKey(), ace); } // Overwrite for (Entry<String, Ace> s : sourceMap.entrySet()) { // TODO Deep copy if (!targetMap.containsKey(s.getKey())) { Ace ace = deepCopy(s.getValue()); ace.setDirect(false); _result.put(s.getKey(), ace); } } // Convert List<Ace> result = new ArrayList<Ace>(); for (Entry<String, Ace> r : _result.entrySet()) { result.add(r.getValue()); } return result; } private HashMap<String, Ace> buildAceMap(List<Ace> aces) { HashMap<String, Ace> map = new HashMap<String, Ace>(); for (Ace ace : aces) { map.put(ace.getPrincipalId(), ace); } return map; } private Ace deepCopy(Ace ace) { Ace result = new Ace(); result.setPrincipalId(ace.getPrincipalId()); result.setDirect(ace.isDirect()); if (CollectionUtils.isEmpty(ace.getPermissions())) { result.setPermissions(new ArrayList<String>()); } else { List<String> l = new ArrayList<String>(); for (String p : ace.getPermissions()) { l.add(p); } result.setPermissions(l); } return result; } private void convertSystemPrincipalId(String repositoryId, List<Ace> aces) { RepositoryInfo info = repositoryInfoMap.get(repositoryId); for (Ace ace : aces) { if (PrincipalId.ANONYMOUS_IN_DB.equals(ace.getPrincipalId())) { String anonymous = info.getPrincipalIdAnonymous(); ace.setPrincipalId(anonymous); } if (PrincipalId.ANYONE_IN_DB.equals(ace.getPrincipalId())) { String anyone = info.getPrincipalIdAnyone(); ace.setPrincipalId(anyone); } } } @Override public Boolean getAclInheritedWithDefault(String repositoryId, Content content){ boolean inheritedAtTopLevel = propertyManager.readBoolean(PropertyKey.CAPABILITY_EXTENDED_PERMISSION_INHERITANCE_TOPLEVEL); if(isRoot(repositoryId, content)){ return false; }else{ if(isTopLevel(repositoryId, content) && !inheritedAtTopLevel){ //default to TRUE return (content.isAclInherited() == null) ? false: content.isAclInherited(); }else{ //default to FALSE return (content.isAclInherited() == null) ? true: content.isAclInherited(); } } } // /////////////////////////////////////// // Change event // /////////////////////////////////////// @Override public Change getChangeEvent(String repositoryId, String changeTokenId) { return contentDaoService.getChangeEvent(repositoryId, changeTokenId); } @Override public List<Change> getLatestChanges(String repositoryId, CallContext context, Holder<String> changeLogToken, Boolean includeProperties, String filter, Boolean includePolicyIds, Boolean includeAcl, BigInteger maxItems, ExtensionsData extension) { return contentDaoService.getLatestChanges(repositoryId, changeLogToken.getValue(), maxItems.intValue()); } @Override public String getLatestChangeToken(String repositoryId) { Change latest = contentDaoService.getLatestChange(repositoryId); if (latest == null) { // TODO null is OK? return null; } else { // return String.valueOf(latest.getChangeToken()); return String.valueOf(latest.getId()); } } // /////////////////////////////////////// // Archive // /////////////////////////////////////// @Override public List<Archive> getAllArchives(String repositoryId) { return contentDaoService.getAllArchives(repositoryId); } @Override public Archive getArchive(String repositoryId, String archiveId) { return contentDaoService.getArchive(repositoryId, archiveId); } @Override public Archive getArchiveByOriginalId(String repositoryId, String originalId) { return contentDaoService.getArchiveByOriginalId(repositoryId, originalId); } @Override public Archive createArchive(CallContext callContext, String repositoryId, String objectId, Boolean deletedWithParent) { Content content = getContent(repositoryId, objectId); // Set base info Archive a = new Archive(); a.setOriginalId(content.getId()); // a.setLastRevision(content.getRevision()); a.setName(content.getName()); a.setType(content.getType()); a.setDeletedWithParent(deletedWithParent); a.setParentId(content.getParentId()); setSignature(callContext, a); // Set Document archive specific info if (content.isDocument()) { Document document = (Document) content; a.setAttachmentNodeId(document.getAttachmentNodeId()); a.setVersionSeriesId(document.getVersionSeriesId()); a.setIsLatestVersion(document.isLatestVersion()); } return contentDaoService.createArchive(repositoryId, a, deletedWithParent); } @Override public Archive createAttachmentArchive(CallContext callContext, String repositoryId, String attachmentId) { Archive a = new Archive(); a.setDeletedWithParent(true); a.setOriginalId(attachmentId); a.setType(NodeType.ATTACHMENT.value()); setSignature(callContext, a); Archive archive = contentDaoService.createAttachmentArchive(repositoryId, a); return archive; } @Override public void restoreArchive(String repositoryId, String archiveId) { Archive archive = contentDaoService.getArchive(repositoryId, archiveId); if (archive == null) { log.error("Archive does not exist!"); return; } // Check whether the destination does still extist. if (!restorationTargetExists(repositoryId, archive)) { log.error("The destination of the restoration doesn't exist"); return; } CallContextImpl dummyContext = new CallContextImpl(null, null, null, null, null, null, null, null); dummyContext.put(dummyContext.USERNAME, PrincipalId.SYSTEM_IN_DB); // Switch over the operation depending on the type of archive if (archive.isFolder()) { Folder restored = restoreFolder(repositoryId, archive); writeChangeEvent(dummyContext, repositoryId, restored, ChangeType.CREATED); } else if (archive.isDocument()) { Document restored = restoreDocument(repositoryId, archive); writeChangeEvent(dummyContext, repositoryId, restored, ChangeType.CREATED); } else if (archive.isAttachment()) { log.error("Attachment can't be restored alone"); } else { log.error("Only document or folder is supported for restoration"); } // Call Solr indexing(optional) solrUtil.callSolrIndexing(repositoryId); } private Document restoreDocument(String repositoryId, Archive archive) { try { // Get archives of the same version series List<Archive> versions = contentDaoService .getArchivesOfVersionSeries(repositoryId, archive.getVersionSeriesId()); for (Archive version : versions) { // Restore a document contentDaoService.restoreContent(repositoryId, version); // Restore its attachment Archive attachmentArchive = contentDaoService.getAttachmentArchive(repositoryId, version); contentDaoService.restoreAttachment(repositoryId, attachmentArchive); // delete archives contentDaoService.deleteArchive(repositoryId, version.getId()); contentDaoService.deleteArchive(repositoryId, attachmentArchive.getId()); } } catch (Exception e) { log.error("fail to restore a document", e); } return getDocument(repositoryId, archive.getOriginalId()); } private Folder restoreFolder(String repositoryId, Archive archive) { contentDaoService.restoreContent(repositoryId, archive); // Restore direct children List<Archive> children = contentDaoService.getChildArchives(repositoryId, archive); if (children != null) { for (Archive child : children) { // Restore descendants recursively // NOTE: Restored only when deletedWithParent flag is true if (child.isDeletedWithParent()) { restoreArchive(repositoryId, child.getId()); } } } contentDaoService.deleteArchive(repositoryId, archive.getId()); return getFolder(repositoryId, archive.getOriginalId()); } private Boolean restorationTargetExists(String repositoryId, Archive archive) { String parentId = archive.getParentId(); Content parent = contentDaoService.getContent(repositoryId, parentId); if (parent == null) { return false; } else { return true; } } // /////////////////////////////////////// // Utility // /////////////////////////////////////// private String buildUniqueName(String repositoryId, String proposedName, String folderId, Content current) { boolean bun = propertyManager.readBoolean(PropertyKey.CAPABILITY_EXTENDED_BUILD_UNIQUE_NAME); if (!bun) { return proposedName; } //Check if update method if(current != null && current.getName().equals(proposedName)){ return proposedName; } List<String>names = contentDaoService.getChildrenNames(repositoryId, folderId); String[] splitted = splitFileName(proposedName); String originalNameBody = splitted[0]; String extension = splitted[1]; String newNameBody = originalNameBody; for(Integer i = 1; i <= names.size(); i++){ if(names.contains(newNameBody + extension)){ newNameBody = originalNameBody + " ~" + i; continue; }else{ break; } } return newNameBody + extension; } private String[] splitFileName(String name) { if (name == null) return null; String body = ""; String suffix = ""; int point = name.lastIndexOf("."); if (point != -1) { body = name.substring(0, point); suffix = "." + name.substring(point + 1); } else { body = name; } String[] ary = { body, suffix }; return ary; } private String increasedVersionLabel(Document document, VersioningState versioningState) { // e.g. #{major}(.{#minor}) String label = document.getVersionLabel(); int major = 0; int minor = 0; int point = label.lastIndexOf("."); if (point == -1) { major = Integer.parseInt(label); } else { major = Integer.parseInt(label.substring(0, point)); minor = Integer.parseInt(label.substring(point + 1)); } String newLabel = label; if (versioningState == VersioningState.MAJOR) { newLabel = String.valueOf(major + 1) + ".0"; } else if (versioningState == VersioningState.MINOR) { newLabel = String.valueOf(major) + "." + String.valueOf(minor + 1); } return newLabel; } private void setSignature(CallContext callContext, NodeBase n) { n.setCreator(callContext.getUsername()); n.setCreated(getTimeStamp()); n.setModifier(callContext.getUsername()); n.setModified(getTimeStamp()); } private void setModifiedSignature(CallContext callContext, NodeBase n) { n.setModifier(callContext.getUsername()); n.setModified(getTimeStamp()); } private GregorianCalendar getTimeStamp() { return DataUtil.millisToCalendar(System.currentTimeMillis()); } public void setRepositoryInfoMap(RepositoryInfoMap repositoryInfoMap) { this.repositoryInfoMap = repositoryInfoMap; } public void setContentDaoService(ContentDaoService contentDaoService) { this.contentDaoService = contentDaoService; } public void setTypeManager(TypeManager typeManager) { this.typeManager = typeManager; } public void setRenditionManager(RenditionManager renditionManager) { this.renditionManager = renditionManager; } public void setPropertyManager(PropertyManager propertyManager) { this.propertyManager = propertyManager; } public void setSolrUtil(SolrUtil solrUtil) { this.solrUtil = solrUtil; } }