/*******************************************************************************
* 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;
}
}