/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * Kind, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ using PortCMIS.Binding; using PortCMIS.Binding.Services; using PortCMIS.Data; using PortCMIS.Data.Extensions; using PortCMIS.Enums; using PortCMIS.Exceptions; using System; using System.Collections.Generic; using System.IO; using System.Numerics; using System.Text; namespace PortCMIS.Client.Impl { /// /// CMIS object base class. /// public abstract class AbstractCmisObject : ICmisObject { /// /// Gets the current session. /// protected ISession Session { get; private set; } /// /// Gets the current repository ID. /// protected string RepositoryId { get { return Session.RepositoryInfo.Id; } } /// /// Gets the current binding. /// protected ICmisBinding Binding { get { return Session.Binding; } } private IObjectType objectType; /// public virtual IObjectType ObjectType { get { lock (objectLock) { return objectType; } } } /// public virtual IList SecondaryTypes { get { lock (objectLock) { return secondaryTypes; } } } /// protected virtual string ObjectId { get { string objectId = Id; if (objectId == null) { throw new InvalidOperationException("Object ID is unknown!"); } return objectId; } } /// /// Gets the operation context that was used to fetch this object. /// protected virtual IOperationContext CreationContext { get; private set; } private IDictionary properties; private IAllowableActions allowableActions; private IList renditions; private IAcl acl; private IList policies; private IList relationships; private IDictionary> extensions; private IList secondaryTypes; /// /// An object used for locking. /// protected object objectLock = new object(); /// /// Initializes the object. /// /// the current session /// the object type /// the low-level object data /// the operation context that was used to fetch this object protected void Initialize(ISession session, IObjectType objectType, IObjectData objectData, IOperationContext context) { if (session == null) { throw new ArgumentNullException("session"); } if (objectType == null) { throw new ArgumentNullException("objectType"); } if (objectType.PropertyDefinitions == null || objectType.PropertyDefinitions.Count < 9) { // there must be at least the 9 standard properties that all objects have throw new ArgumentException("Object type must have property definitions!"); } if (objectData == null) { throw new ArgumentNullException("objectData"); } if (objectData.Properties == null) { throw new ArgumentException("Object Data properties must be set!"); } if (objectData.Id == null) { throw new ArgumentException("Object ID must be set!"); } this.Session = session; this.objectType = objectType; this.extensions = new Dictionary>(); this.CreationContext = new OperationContext(context); this.RefreshTimestamp = DateTime.UtcNow; IObjectFactory of = Session.ObjectFactory; if (objectData != null) { // handle properties if (objectData.Properties != null) { // search secondaryObjectTypes foreach (IPropertyData property in objectData.Properties.PropertyList) { if (property.Id == PropertyIds.SecondaryObjectTypeIds) { IList stids = property.Values as IList; if (stids != null && stids.Count > 0) { secondaryTypes = new List(); foreach (object stid in stids) { IObjectType type = Session.GetTypeDefinition(stid.ToString()); if (type is ISecondaryType) { secondaryTypes.Add(type as ISecondaryType); } } } else { secondaryTypes = null; } break; } } properties = of.ConvertProperties(objectType, secondaryTypes, objectData.Properties); extensions[ExtensionLevel.Properties] = objectData.Properties.Extensions; } // handle allowable actions if (objectData.AllowableActions != null) { allowableActions = objectData.AllowableActions; extensions[ExtensionLevel.AllowableActions] = objectData.AllowableActions.Extensions; } else { allowableActions = null; } // handle renditions if (objectData.Renditions != null) { renditions = new List(); foreach (IRenditionData rd in objectData.Renditions) { renditions.Add(of.ConvertRendition(Id, rd)); } } else { renditions = null; } // handle Acl if (objectData.Acl != null) { acl = objectData.Acl; extensions[ExtensionLevel.Acl] = objectData.Acl.Extensions; } else { acl = null; } // handle policies if (objectData.PolicyIds != null && objectData.PolicyIds.PolicyIds != null) { policies = new List(); foreach (string pid in objectData.PolicyIds.PolicyIds) { IPolicy policy = Session.GetObject(Session.CreateObjectId(pid)) as IPolicy; if (policy != null) { policies.Add(policy); } } extensions[ExtensionLevel.Policies] = objectData.PolicyIds.Extensions; } else { policies = null; } // handle relationships if (objectData.Relationships != null) { relationships = new List(); foreach (IObjectData rod in objectData.Relationships) { IRelationship relationship = of.ConvertObject(rod, CreationContext) as IRelationship; if (relationship != null) { relationships.Add(relationship); } } } else { relationships = null; } extensions[ExtensionLevel.Object] = objectData.Extensions; } } /// /// Returns the query name of a property. /// /// the property ID /// the query name or null if the property doesn't exist or the property has no query name protected virtual string GetPropertyQueryName(string propertyId) { lock (objectLock) { IPropertyDefinition propDef = objectType[propertyId]; if (propDef == null) { return null; } return propDef.QueryName; } } // --- object --- /// public virtual void Delete() { Delete(true); } /// public virtual void Delete(bool allVersions) { lock (objectLock) { Session.Delete(this, allVersions); } } /// public virtual ICmisObject UpdateProperties(IDictionary properties) { IObjectId objectId = UpdateProperties(properties, true); if (objectId == null) { return null; } if (ObjectId != objectId.Id) { return Session.GetObject(objectId, CreationContext); } return this; } /// public virtual IObjectId UpdateProperties(IDictionary properties, bool refresh) { if (properties == null || properties.Count == 0) { throw new ArgumentException("Properties must not be empty!"); } string newObjectId = null; lock (objectLock) { string objectId = ObjectId; string changeToken = ChangeToken; HashSet updatebility = new HashSet(); updatebility.Add(Updatability.ReadWrite); // check if checked out bool? isCheckedOut = GetPropertyValue(PropertyIds.IsVersionSeriesCheckedOut) as bool?; if (isCheckedOut.HasValue && isCheckedOut.Value) { updatebility.Add(Updatability.WhenCheckedOut); } // it's time to update Binding.GetObjectService().UpdateProperties(RepositoryId, ref objectId, ref changeToken, Session.ObjectFactory.ConvertProperties(properties, this.objectType, this.secondaryTypes, updatebility), null); newObjectId = objectId; } if (refresh) { Refresh(); } if (newObjectId == null) { return null; } return Session.CreateObjectId(newObjectId); } /// public virtual ICmisObject Rename(string newName) { if (newName == null || newName.Length == 0) { throw new ArgumentException("New name must not be empty!", "newName"); } IDictionary prop = new Dictionary(); prop[PropertyIds.Name] = newName; return UpdateProperties(prop); } /// public virtual IObjectId Rename(string newName, bool refresh) { IDictionary prop = new Dictionary(); prop[PropertyIds.Name] = newName; return UpdateProperties(prop, refresh); } // --- properties --- /// public virtual IObjectType BaseType { get { return Session.GetTypeDefinition(GetPropertyAsStringValue(PropertyIds.BaseTypeId)); } } /// public virtual BaseTypeId BaseTypeId { get { string baseType = GetPropertyAsStringValue(PropertyIds.BaseTypeId); if (baseType == null) { throw new InvalidOperationException("Base type not set!"); } return baseType.GetCmisEnum(); } } /// public virtual string Id { get { return GetPropertyAsStringValue(PropertyIds.ObjectId); } } /// public virtual string Name { get { return GetPropertyAsStringValue(PropertyIds.Name); } } /// public virtual string CreatedBy { get { return GetPropertyAsStringValue(PropertyIds.CreatedBy); } } /// public virtual DateTime? CreationDate { get { return GetPropertyAsDateTimeValue(PropertyIds.CreationDate); } } /// public virtual string LastModifiedBy { get { return GetPropertyAsStringValue(PropertyIds.LastModifiedBy); } } /// public virtual DateTime? LastModificationDate { get { return GetPropertyAsDateTimeValue(PropertyIds.LastModificationDate); } } /// public virtual string ChangeToken { get { return GetPropertyAsStringValue(PropertyIds.ChangeToken); } } /// public virtual IList Properties { get { lock (objectLock) { return new List(properties.Values); } } } /// public virtual IProperty this[string propertyId] { get { if (propertyId == null) { throw new ArgumentNullException("propertyId"); } lock (objectLock) { IProperty property; if (properties.TryGetValue(propertyId, out property)) { return property; } return null; } } } /// public virtual object GetPropertyValue(string propertyId) { IProperty property = this[propertyId]; if (property == null) { return null; } return property.Value; } /// public virtual string GetPropertyAsStringValue(string propertyId) { object value = GetPropertyValue(propertyId); if (value == null) { return null; } return Convert.ToString(value); } /// public virtual long? GetPropertyAsLongValue(string propertyId) { object value = GetPropertyValue(propertyId); if (value == null) { return null; } if (value is BigInteger) { return (long)((BigInteger)value); } return Convert.ToInt64(value); } /// public virtual bool? GetPropertyAsBoolValue(string propertyId) { object value = GetPropertyValue(propertyId); if (value == null) { return null; } if (value is BigInteger) { return Convert.ToBoolean((long)((BigInteger)value)); } return Convert.ToBoolean(value); } /// public virtual DateTime? GetPropertyAsDateTimeValue(string propertyId) { object value = GetPropertyValue(propertyId); if (value == null) { return null; } return Convert.ToDateTime(value); } // --- allowable actions --- /// public virtual IAllowableActions AllowableActions { get { lock (objectLock) { return allowableActions; } } } /// public virtual bool HasAllowableAction(PortCMIS.Enums.Action action) { IAllowableActions currentAllowableActions = AllowableActions; if (currentAllowableActions == null || currentAllowableActions.Actions == null) { throw new Exception("Allowable Actions are not available!"); } return currentAllowableActions.Actions.Contains(action); } // --- renditions --- /// public virtual IList Renditions { get { lock (objectLock) { return renditions; } } } // --- Acl --- /// public virtual IAcl GetAcl(bool onlyBasicPermissions) { return Binding.GetAclService().GetAcl(RepositoryId, ObjectId, onlyBasicPermissions, null); } /// public virtual IAcl ApplyAcl(IList addAces, IList removeAces, AclPropagation? aclPropagation) { IAcl result = Session.ApplyAcl(this, addAces, removeAces, aclPropagation); Refresh(); return result; } /// public virtual IAcl AddAcl(IList addAces, AclPropagation? aclPropagation) { return ApplyAcl(addAces, null, aclPropagation); } /// public virtual IAcl RemoveAcl(IList removeAces, AclPropagation? aclPropagation) { return ApplyAcl(null, removeAces, aclPropagation); } /// public virtual IAcl Acl { get { lock (objectLock) { return acl; } } } // --- policies --- /// public virtual void ApplyPolicy(params IObjectId[] policyId) { lock (objectLock) { Session.ApplyPolicy(this, policyId); } Refresh(); } /// public virtual void RemovePolicy(params IObjectId[] policyId) { lock (objectLock) { Session.RemovePolicy(this, policyId); } Refresh(); } /// public virtual IList Policies { get { lock (objectLock) { return policies; } } } // --- relationships --- /// public virtual IList Relationships { get { lock (objectLock) { return relationships; } } } // --- extensions --- /// public virtual IList GetExtensions(ExtensionLevel level) { IList ext; if (extensions.TryGetValue(level, out ext)) { return ext; } return null; } // --- other --- /// public virtual DateTime RefreshTimestamp { get; private set; } /// public virtual void Refresh() { lock (objectLock) { IOperationContext oc = CreationContext; // get the latest data from the repository IObjectData objectData = Binding.GetObjectService().GetObject(RepositoryId, ObjectId, oc.FilterString, oc.IncludeAllowableActions, oc.IncludeRelationships, oc.RenditionFilterString, oc.IncludePolicies, oc.IncludeAcls, null); // reset this object Initialize(Session, ObjectType, objectData, CreationContext); } } /// public virtual void RefreshIfOld(long durationInMillis) { lock (objectLock) { if (((DateTime.UtcNow - RefreshTimestamp).Ticks / 10000) > durationInMillis) { Refresh(); } } } /// public override string ToString() { lock (objectLock) { if (objectType == null) { return ""; } return objectType.BaseTypeId + " (" + objectType.Id + "): " + Id; } } } /// /// Fileable object base class. /// public abstract class AbstractFileableCmisObject : AbstractCmisObject, IFileableCmisObject { /// public virtual IFileableCmisObject Move(IObjectId sourceFolderId, IObjectId targetFolderId) { string objectId = ObjectId; if (sourceFolderId == null || sourceFolderId.Id == null) { throw new ArgumentException("Source folder ID must be set!", "sourceFolderId"); } if (targetFolderId == null || targetFolderId.Id == null) { throw new ArgumentException("Target folder ID must be set!", "targetFolderId"); } Binding.GetObjectService().MoveObject(RepositoryId, ref objectId, targetFolderId.Id, sourceFolderId.Id, null); if (objectId == null) { return null; } IFileableCmisObject movedObject = Session.GetObject(Session.CreateObjectId(objectId)) as IFileableCmisObject; if (movedObject == null) { throw new CmisRuntimeException("Moved object is invalid!"); } return movedObject; } /// public virtual IList Parents { get { // get object ids of the parent folders IList bindingParents = Binding.GetNavigationService().GetObjectParents(RepositoryId, ObjectId, GetPropertyQueryName(PropertyIds.ObjectId), false, IncludeRelationships.None, null, false, null); IList parents = new List(); foreach (IObjectParentData p in bindingParents) { if (p == null || p.Object == null || p.Object.Properties == null) { // should not happen... throw new CmisInvalidServerData("Repository sent invalid data!"); } // get id property IPropertyData idProperty = p.Object.Properties[PropertyIds.ObjectId]; if (idProperty == null || idProperty.PropertyType != PropertyType.Id) { // the repository sent an object without a valid object id... throw new CmisInvalidServerData("Repository sent invalid data! No object ID!"); } // fetch the object and make sure it is a folder IObjectId parentId = Session.CreateObjectId(idProperty.FirstValue as string); IFolder parentFolder = Session.GetObject(parentId) as IFolder; if (parentFolder == null) { // the repository sent an object that is not a folder... throw new CmisInvalidServerData("Repository sent invalid data! Object is not a folder!"); } parents.Add(parentFolder); } return parents; } } /// public virtual IList Paths { get { // get object paths of the parent folders IList parents = Binding.GetNavigationService().GetObjectParents( RepositoryId, ObjectId, GetPropertyQueryName(PropertyIds.Path), false, IncludeRelationships.None, null, true, null); IList paths = new List(); foreach (IObjectParentData p in parents) { if (p == null || p.Object == null || p.Object.Properties == null) { // should not happen... throw new CmisInvalidServerData("Repository sent invalid data!"); } // get path property IPropertyData pathProperty = p.Object.Properties[PropertyIds.Path]; if (pathProperty == null || pathProperty.PropertyType != PropertyType.String) { // the repository sent a folder without a valid path... throw new CmisInvalidServerData("Repository sent invalid data! No path property!"); } if (p.RelativePathSegment == null) { // the repository didn't send a relative path segment throw new CmisInvalidServerData("Repository sent invalid data! No relative path segment!"); } string folderPath = pathProperty.FirstValue as string; if (folderPath == null) { // the repository sent a folder without a valid path... throw new CmisInvalidServerData("Repository sent invalid data! No path property value!"); } paths.Add(folderPath + (folderPath.EndsWith("/") ? "" : "/") + p.RelativePathSegment); } return paths; } } /// public virtual void AddToFolder(IObjectId folderId, bool allVersions) { if (folderId == null || folderId.Id == null) { throw new ArgumentException("Folder ID must be set!"); } Binding.GetMultiFilingService().AddObjectToFolder(RepositoryId, ObjectId, folderId.Id, allVersions, null); } /// public virtual void RemoveFromFolder(IObjectId folderId) { Binding.GetMultiFilingService().RemoveObjectFromFolder(RepositoryId, ObjectId, folderId == null ? null : folderId.Id, null); } } /// /// Document implementation. /// public class Document : AbstractFileableCmisObject, IDocument { /// /// Constructor. /// /// the session object /// the object type /// the low-level data object /// the operation context used to fetch this object public Document(ISession session, IObjectType objectType, IObjectData objectData, IOperationContext context) { Initialize(session, objectType, objectData, context); } // properties /// public virtual bool? IsImmutable { get { return GetPropertyAsBoolValue(PropertyIds.IsImmutable); } } /// public virtual bool? IsLatestVersion { get { return GetPropertyAsBoolValue(PropertyIds.IsLatestVersion); } } /// public virtual bool? IsMajorVersion { get { return GetPropertyAsBoolValue(PropertyIds.IsMajorVersion); } } /// public virtual bool? IsLatestMajorVersion { get { return GetPropertyAsBoolValue(PropertyIds.IsLatestMajorVersion); } } /// public virtual string VersionLabel { get { return GetPropertyAsStringValue(PropertyIds.VersionLabel); } } /// public virtual string VersionSeriesId { get { return GetPropertyAsStringValue(PropertyIds.VersionSeriesId); } } /// public virtual bool? IsVersionSeriesCheckedOut { get { return GetPropertyAsBoolValue(PropertyIds.IsVersionSeriesCheckedOut); } } /// public virtual string VersionSeriesCheckedOutBy { get { return GetPropertyAsStringValue(PropertyIds.VersionSeriesCheckedOutBy); } } /// public virtual string VersionSeriesCheckedOutId { get { return GetPropertyAsStringValue(PropertyIds.VersionSeriesCheckedOutId); } } /// public virtual string CheckinComment { get { return GetPropertyAsStringValue(PropertyIds.CheckinComment); } } /// public virtual long? ContentStreamLength { get { return GetPropertyAsLongValue(PropertyIds.ContentStreamLength); } } /// public virtual string ContentStreamMimeType { get { return GetPropertyAsStringValue(PropertyIds.ContentStreamMimeType); } } /// public virtual string ContentStreamFileName { get { return GetPropertyAsStringValue(PropertyIds.ContentStreamFileName); } } /// public virtual string ContentStreamId { get { return GetPropertyAsStringValue(PropertyIds.ContentStreamId); } } /// public virtual IList ContentStreamHashes { get { IProperty hashes = this[PropertyIds.ContentStreamHash]; if (hashes == null || hashes.Values == null || hashes.Values.Count == 0) { return null; } IList result = new List(); foreach (object hash in hashes.Values) { result.Add(new ContentStreamHash(Convert.ToString(hash))); } return result; } } // operations /// public virtual IDocument Copy(IObjectId targetFolderId, IDictionary properties, VersioningState? versioningState, IList policies, IList addAces, IList removeAces, IOperationContext context) { IObjectId newId; try { newId = Session.CreateDocumentFromSource(this, properties, targetFolderId, versioningState, policies, addAces, removeAces); } catch (CmisNotSupportedException) { newId = CopyViaClient(targetFolderId, properties, versioningState, policies, addAces, removeAces); } // if no context is provided the object will not be fetched if (context == null || newId == null) { return null; } // get the new object IDocument newDoc = Session.GetObject(newId, context) as IDocument; if (newDoc == null) { throw new CmisRuntimeException("Newly created object is not a document! New ID: " + newId); } return newDoc; } /// public virtual IDocument Copy(IObjectId targetFolderId) { return Copy(targetFolderId, null, null, null, null, null, Session.DefaultContext); } /// /// Copies the document manually. The content is streamed from the repository and back. /// protected virtual IObjectId CopyViaClient(IObjectId targetFolderId, IDictionary properties, VersioningState? versioningState, IList policies, IList addAces, IList removeAces) { IDictionary newProperties = new Dictionary(); IOperationContext allPropsContext = Session.CreateOperationContext(); allPropsContext.FilterString = "*"; allPropsContext.IncludeAcls = false; allPropsContext.IncludeAllowableActions = false; allPropsContext.IncludePathSegments = false; allPropsContext.IncludePolicies = false; allPropsContext.IncludeRelationships = IncludeRelationships.None; allPropsContext.RenditionFilterString = "cmis:none"; IDocument allPropsDoc = (IDocument)Session.GetObject(this, allPropsContext); foreach (IProperty prop in allPropsDoc.Properties) { if (prop.PropertyDefinition.Updatability == Updatability.ReadWrite || prop.PropertyDefinition.Updatability == Updatability.OnCreate) { newProperties.Add(prop.Id, prop.Value); } } if (properties != null) { foreach (KeyValuePair prop in properties) { newProperties[prop.Key] = prop.Value; } } IContentStream contentStream = allPropsDoc.GetContentStream(); try { return Session.CreateDocument(newProperties, targetFolderId, contentStream, versioningState, policies, addAces, removeAces); } finally { if (contentStream != null) { Stream stream = contentStream.Stream; if (stream != null) { stream.Dispose(); } } } } /// public virtual void DeleteAllVersions() { Delete(true); } // versioning /// public virtual IObjectId CheckOut() { string newObjectId = null; lock (objectLock) { string objectId = ObjectId; bool? contentCopied; Binding.GetVersioningService().CheckOut(RepositoryId, ref objectId, null, out contentCopied); newObjectId = objectId; } if (newObjectId == null) { return null; } return Session.CreateObjectId(newObjectId); } /// public virtual void CancelCheckOut() { Binding.GetVersioningService().CancelCheckOut(RepositoryId, ObjectId, null); } /// public virtual IObjectId CheckIn(bool major, IDictionary properties, IContentStream contentStream, string checkinComment, IList policies, IList addAces, IList removeAces) { String newObjectId = null; lock (objectLock) { string objectId = ObjectId; IObjectFactory of = Session.ObjectFactory; HashSet updatebility = new HashSet(); updatebility.Add(Updatability.ReadWrite); updatebility.Add(Updatability.WhenCheckedOut); Binding.GetVersioningService().CheckIn(RepositoryId, ref objectId, major, of.ConvertProperties(properties, ObjectType, SecondaryTypes, updatebility), contentStream, checkinComment, of.ConvertPolicies(policies), of.ConvertAces(addAces), of.ConvertAces(removeAces), null); newObjectId = objectId; } if (newObjectId == null) { return null; } return Session.CreateObjectId(newObjectId); } /// public virtual IList GetAllVersions() { return GetAllVersions(Session.DefaultContext); } /// public virtual IList GetAllVersions(IOperationContext context) { string objectId; string versionSeriesId; lock (objectLock) { objectId = ObjectId; versionSeriesId = VersionSeriesId; } IList versions = Binding.GetVersioningService().GetAllVersions(RepositoryId, objectId, versionSeriesId, context.FilterString, context.IncludeAllowableActions, null); IObjectFactory of = Session.ObjectFactory; IList result = new List(); if (versions != null) { foreach (IObjectData objectData in versions) { IDocument doc = of.ConvertObject(objectData, context) as IDocument; if (doc == null) { // should not happen... continue; } result.Add(doc); } } return result; } /// public virtual IDocument GetObjectOfLatestVersion(bool major) { return GetObjectOfLatestVersion(major, Session.DefaultContext); } /// public virtual IDocument GetObjectOfLatestVersion(bool major, IOperationContext context) { return Session.GetLatestDocumentVersion(this, major, context); } // content operations /// public virtual IContentStream GetContentStream() { return GetContentStream(null); } /// public virtual IContentStream GetContentStream(string streamId) { return GetContentStream(streamId, null, null); } /// public virtual IContentStream GetContentStream(string streamId, long? offset, long? length) { IContentStream contentStream = Session.GetContentStream(this, streamId, offset, length); if (contentStream == null) { // no content stream return null; } // the AtomPub binding doesn't return a file name // -> get the file name from properties, if present if (contentStream.FileName == null && ContentStreamFileName != null) { ContentStream newContentStream = new ContentStream(); newContentStream.FileName = ContentStreamFileName; newContentStream.Length = contentStream.Length; newContentStream.MimeType = contentStream.MimeType; newContentStream.Stream = contentStream.Stream; newContentStream.Extensions = contentStream.Extensions; contentStream = newContentStream; } return contentStream; } /// public virtual IDocument SetContentStream(IContentStream contentStream, bool overwrite) { IObjectId objectId = SetContentStream(contentStream, overwrite, true); if (objectId == null) { return null; } if (ObjectId != objectId.Id) { return (IDocument)Session.GetObject(objectId, CreationContext); } return this; } /// public virtual IObjectId SetContentStream(IContentStream contentStream, bool overwrite, bool refresh) { string newObjectId = null; lock (objectLock) { string objectId = ObjectId; string changeToken = ChangeToken; Binding.GetObjectService().SetContentStream(RepositoryId, ref objectId, overwrite, ref changeToken, contentStream, null); newObjectId = objectId; } if (refresh) { Refresh(); } if (newObjectId == null) { return null; } return Session.CreateObjectId(newObjectId); } /// public virtual IDocument AppendContentStream(IContentStream contentStream, bool isLastChunk) { IObjectId objectId = AppendContentStream(contentStream, isLastChunk, true); if (objectId == null) { return null; } if (ObjectId != objectId.Id) { return (IDocument)Session.GetObject(objectId, CreationContext); } return this; } /// public virtual IObjectId AppendContentStream(IContentStream contentStream, bool isLastChunk, bool refresh) { string newObjectId = null; lock (objectLock) { string objectId = ObjectId; string changeToken = ChangeToken; Binding.GetObjectService().AppendContentStream(RepositoryId, ref objectId, isLastChunk, ref changeToken, contentStream, null); newObjectId = objectId; } if (refresh) { Refresh(); } if (newObjectId == null) { return null; } return Session.CreateObjectId(newObjectId); } /// public virtual IDocument DeleteContentStream() { IObjectId objectId = DeleteContentStream(true); if (objectId == null) { return null; } if (ObjectId != objectId.Id) { return (IDocument)Session.GetObject(objectId, CreationContext); } return this; } /// public virtual IObjectId DeleteContentStream(bool refresh) { string newObjectId = null; lock (objectLock) { string objectId = ObjectId; string changeToken = ChangeToken; Binding.GetObjectService().DeleteContentStream(RepositoryId, ref objectId, ref changeToken, null); newObjectId = objectId; } if (refresh) { Refresh(); } if (newObjectId == null) { return null; } return Session.CreateObjectId(newObjectId); } /// public virtual IObjectId CheckIn(bool major, IDictionary properties, IContentStream contentStream, string checkinComment) { return this.CheckIn(major, properties, contentStream, checkinComment, null, null, null); } } /// /// Folder implementation. /// public class Folder : AbstractFileableCmisObject, IFolder { /// /// Constructor. /// /// the session object /// the object type /// the low-level data object /// the operation context used to fetch this object public Folder(ISession session, IObjectType objectType, IObjectData objectData, IOperationContext context) { Initialize(session, objectType, objectData, context); } /// public virtual IDocument CreateDocument(IDictionary properties, IContentStream contentStream, VersioningState? versioningState, IList policies, IList addAces, IList removeAces, IOperationContext context) { IObjectId newId = Session.CreateDocument(properties, this, contentStream, versioningState, policies, addAces, removeAces); // if no context is provided the object will not be fetched if (context == null || newId == null) { return null; } // get the new object IDocument newDoc = Session.GetObject(newId, context) as IDocument; if (newDoc == null) { throw new CmisRuntimeException("Newly created object is not a document! New ID: " + newId); } return newDoc; } /// public virtual IDocument CreateDocumentFromSource(IObjectId source, IDictionary properties, VersioningState? versioningState, IList policies, IList addAces, IList removeAces, IOperationContext context) { IObjectId newId = Session.CreateDocumentFromSource(source, properties, this, versioningState, policies, addAces, removeAces); // if no context is provided the object will not be fetched if (context == null || newId == null) { return null; } // get the new object IDocument newDoc = Session.GetObject(newId, context) as IDocument; if (newDoc == null) { throw new CmisRuntimeException("Newly created object is not a document! New ID: " + newId); } return newDoc; } /// public virtual IFolder CreateFolder(IDictionary properties, IList policies, IList addAces, IList removeAces, IOperationContext context) { IObjectId newId = Session.CreateFolder(properties, this, policies, addAces, removeAces); // if no context is provided the object will not be fetched if (context == null || newId == null) { return null; } // get the new object IFolder newFolder = Session.GetObject(newId, context) as IFolder; if (newFolder == null) { throw new CmisRuntimeException("Newly created object is not a folder! New ID: " + newId); } return newFolder; } /// public virtual IPolicy CreatePolicy(IDictionary properties, IList policies, IList addAces, IList removeAces, IOperationContext context) { IObjectId newId = Session.CreatePolicy(properties, this, policies, addAces, removeAces); // if no context is provided the object will not be fetched if (context == null || newId == null) { return null; } // get the new object IPolicy newPolicy = Session.GetObject(newId, context) as IPolicy; if (newPolicy == null) { throw new CmisRuntimeException("Newly created object is not a policy! New ID: " + newId); } return newPolicy; } /// public virtual IItem CreateItem(IDictionary properties, IList policies, IList addAces, IList removeAces, IOperationContext context) { IObjectId newId = Session.CreateItem(properties, this, policies, addAces, removeAces); // if no context is provided the object will not be fetched if (context == null || newId == null) { return null; } // get the new object IItem newItem = Session.GetObject(newId, context) as IItem; if (newItem == null) { throw new CmisRuntimeException("Newly created object is not an item! New ID: " + newId); } return newItem; } /// public virtual IList DeleteTree(bool allVersions, UnfileObject? unfile, bool continueOnFailure) { return Session.DeleteTree(this, allVersions, unfile, continueOnFailure); } /// public virtual string ParentId { get { return GetPropertyAsStringValue(PropertyIds.ParentId); } } /// public virtual IList AllowedChildObjectTypes { get { IList result = new List(); lock (objectLock) { IList otids = GetPropertyValue(PropertyIds.AllowedChildObjectTypeIds) as IList; if (otids == null) { return result; } foreach (string otid in otids) { result.Add(Session.GetTypeDefinition(otid)); } } return result; } } /// public virtual IItemEnumerable GetCheckedOutDocs() { return GetCheckedOutDocs(Session.DefaultContext); } /// public virtual IItemEnumerable GetCheckedOutDocs(IOperationContext context) { string objectId = ObjectId; INavigationService service = Binding.GetNavigationService(); IObjectFactory of = Session.ObjectFactory; IOperationContext ctxt = new OperationContext(context); PageFetcher.FetchPage fetchPageDelegate = delegate(BigInteger maxNumItems, BigInteger skipCount) { // get checked out documents for this folder IObjectList checkedOutDocs = service.GetCheckedOutDocs(RepositoryId, objectId, ctxt.FilterString, ctxt.OrderBy, ctxt.IncludeAllowableActions, ctxt.IncludeRelationships, ctxt.RenditionFilterString, maxNumItems, skipCount, null); IList page = new List(); if (checkedOutDocs.Objects != null) { foreach (IObjectData objectData in checkedOutDocs.Objects) { IDocument doc = of.ConvertObject(objectData, ctxt) as IDocument; if (doc == null) { // should not happen... continue; } page.Add(doc); } } return new PageFetcher.Page(page, checkedOutDocs.NumItems, checkedOutDocs.HasMoreItems); }; return new CollectionEnumerable(new PageFetcher(ctxt.MaxItemsPerPage, fetchPageDelegate)); } /// public virtual IItemEnumerable GetChildren() { return GetChildren(Session.DefaultContext); } /// public virtual IItemEnumerable GetChildren(IOperationContext context) { string objectId = ObjectId; INavigationService service = Binding.GetNavigationService(); IObjectFactory of = Session.ObjectFactory; IOperationContext ctxt = new OperationContext(context); PageFetcher.FetchPage fetchPageDelegate = delegate(BigInteger maxNumItems, BigInteger skipCount) { // get the children IObjectInFolderList children = service.GetChildren(RepositoryId, objectId, ctxt.FilterString, ctxt.OrderBy, ctxt.IncludeAllowableActions, ctxt.IncludeRelationships, ctxt.RenditionFilterString, ctxt.IncludePathSegments, maxNumItems, skipCount, null); // convert objects IList page = new List(); if (children.Objects != null) { foreach (IObjectInFolderData objectData in children.Objects) { if (objectData.Object != null) { page.Add(of.ConvertObject(objectData.Object, ctxt)); } } } return new PageFetcher.Page(page, children.NumItems, children.HasMoreItems); }; return new CollectionEnumerable(new PageFetcher(ctxt.MaxItemsPerPage, fetchPageDelegate)); } /// public virtual IList> GetDescendants(int depth) { return GetDescendants(depth, Session.DefaultContext); } /// public virtual IList> GetDescendants(int depth, IOperationContext context) { IList bindingContainerList = Binding.GetNavigationService().GetDescendants(RepositoryId, ObjectId, depth, context.FilterString, context.IncludeAllowableActions, context.IncludeRelationships, context.RenditionFilterString, context.IncludePathSegments, null); return ConvertProviderContainer(bindingContainerList, context); } /// public virtual IList> GetFolderTree(int depth) { return GetFolderTree(depth, Session.DefaultContext); } /// public virtual IList> GetFolderTree(int depth, IOperationContext context) { IList bindingContainerList = Binding.GetNavigationService().GetFolderTree(RepositoryId, ObjectId, depth, context.FilterString, context.IncludeAllowableActions, context.IncludeRelationships, context.RenditionFilterString, context.IncludePathSegments, null); return ConvertProviderContainer(bindingContainerList, context); } private IList> ConvertProviderContainer(IList bindingContainerList, IOperationContext context) { if (bindingContainerList == null || bindingContainerList.Count == 0) { return null; } IList> result = new List>(); foreach (IObjectInFolderContainer oifc in bindingContainerList) { if (oifc.Object == null || oifc.Object.Object == null) { // shouldn't happen ... continue; } // convert the object IFileableCmisObject cmisObject = Session.ObjectFactory.ConvertObject(oifc.Object.Object, context) as IFileableCmisObject; if (cmisObject == null) { // the repository must not return objects that are not fileable, but you never know... continue; } // convert the children IList> children = ConvertProviderContainer(oifc.Children, context); // add both to current container Tree tree = new Tree(); tree.Item = cmisObject; tree.Children = children; result.Add(tree); } return result; } /// public virtual bool IsRootFolder { get { return ObjectId == Session.RepositoryInfo.RootFolderId; } } /// public virtual IFolder FolderParent { get { if (IsRootFolder) { return null; } IList parents = Parents; if (parents == null || parents.Count == 0) { return null; } return parents[0]; } } /// public virtual string Path { get { string path; lock (objectLock) { // get the path property path = GetPropertyAsStringValue(PropertyIds.Path); // if the path property isn't set, get it if (path == null) { IObjectData objectData = Binding.GetObjectService().GetObject(RepositoryId, ObjectId, GetPropertyQueryName(PropertyIds.Path), false, IncludeRelationships.None, "cmis:none", false, false, null); if (objectData.Properties != null) { IPropertyData pathProperty = objectData.Properties[PropertyIds.Path]; if (pathProperty != null && pathProperty.PropertyType == PropertyType.String) { path = pathProperty.FirstValue as string; } } } } // we still don't know the path ... it's not a CMIS compliant repository if (path == null) { throw new CmisInvalidServerData("Repository didn't return " + PropertyIds.Path + "!"); } return path; } } /// public override IList Paths { get { IList result = new List(); result.Add(Path); return result; } } /// public virtual IDocument CreateDocument(IDictionary properties, IContentStream contentStream, VersioningState? versioningState) { return CreateDocument(properties, contentStream, versioningState, null, null, null, Session.DefaultContext); } /// public virtual IDocument CreateDocumentFromSource(IObjectId source, IDictionary properties, VersioningState? versioningState) { return CreateDocumentFromSource(source, properties, versioningState, null, null, null, Session.DefaultContext); } /// public virtual IFolder CreateFolder(IDictionary properties) { return CreateFolder(properties, null, null, null, Session.DefaultContext); } /// public virtual IPolicy CreatePolicy(IDictionary properties) { return CreatePolicy(properties, null, null, null, Session.DefaultContext); } /// public virtual IItem CreateItem(IDictionary properties) { return CreateItem(properties, null, null, null, Session.DefaultContext); } } /// /// Policy implementation. /// public class Policy : AbstractFileableCmisObject, IPolicy { /// /// Constructor. /// /// the session object /// the object type /// the low-level data object /// the operation context used to fetch this object public Policy(ISession session, IObjectType objectType, IObjectData objectData, IOperationContext context) { Initialize(session, objectType, objectData, context); } /// public virtual string PolicyText { get { return GetPropertyAsStringValue(PropertyIds.PolicyText); } } } /// /// Relationship implementation. /// public class Relationship : AbstractCmisObject, IRelationship { /// /// Constructor. /// /// the session object /// the object type /// the low-level data object /// the operation context used to fetch this object public Relationship(ISession session, IObjectType objectType, IObjectData objectData, IOperationContext context) { Initialize(session, objectType, objectData, context); } /// public virtual ICmisObject GetSource() { return GetSource(Session.DefaultContext); } /// public virtual ICmisObject GetSource(IOperationContext context) { lock (objectLock) { IObjectId sourceId = SourceId; if (sourceId == null) { return null; } return Session.GetObject(sourceId, context); } } /// public virtual IObjectId SourceId { get { string sourceId = GetPropertyAsStringValue(PropertyIds.SourceId); if (sourceId == null || sourceId.Length == 0) { return null; } return Session.CreateObjectId(sourceId); } } /// public virtual ICmisObject GetTarget() { return GetTarget(Session.DefaultContext); } /// public virtual ICmisObject GetTarget(IOperationContext context) { lock (objectLock) { IObjectId targetId = TargetId; if (targetId == null) { return null; } return Session.GetObject(targetId, context); } } /// public virtual IObjectId TargetId { get { string targetId = GetPropertyAsStringValue(PropertyIds.TargetId); if (targetId == null || targetId.Length == 0) { return null; } return Session.CreateObjectId(targetId); } } } /// /// Item implementation. /// public class Item : AbstractFileableCmisObject, IItem { /// /// Constructor. /// /// the session object /// the object type /// the low-level data object /// the operation context used to fetch this object public Item(ISession session, IObjectType objectType, IObjectData objectData, IOperationContext context) { Initialize(session, objectType, objectData, context); } } /// /// Property implementation. /// public class Property : IProperty { /// /// Constructor. /// /// the property definition /// the property values public Property(IPropertyDefinition propertyDefinition, IList values) { PropertyDefinition = propertyDefinition; Values = values; } /// public string Id { get { return PropertyDefinition.Id; } } /// public string LocalName { get { return PropertyDefinition.LocalName; } } /// public string DisplayName { get { return PropertyDefinition.DisplayName; } } /// public string QueryName { get { return PropertyDefinition.QueryName; } } /// public bool IsMultiValued { get { return PropertyDefinition.Cardinality == Cardinality.Multi; } } /// public PropertyType? PropertyType { get { return PropertyDefinition.PropertyType; } } /// public IPropertyDefinition PropertyDefinition { get; protected set; } /// public object Value { get { if (PropertyDefinition.Cardinality == Cardinality.Single) { return Values == null || Values.Count == 0 ? null : Values[0]; } else { return Values; } } } /// public IList Values { get; protected set; } /// public object FirstValue { get { return Values == null || Values.Count == 0 ? null : Values[0]; } } /// public string ValueAsString { get { return FormatValue(FirstValue); } } /// public string ValuesAsString { get { if (Values == null) { return "[]"; } else { StringBuilder result = new StringBuilder(); foreach (object value in Values) { if (result.Length > 0) { result.Append(", "); } result.Append(FormatValue(value)); } return "[" + result.ToString() + "]"; } } } private string FormatValue(object value) { if (value == null) { return null; } // for future formating return value.ToString(); } /// public override string ToString() { return Id + ": " + ValuesAsString; } } /// /// Rendition implementation. /// public class Rendition : RenditionData, IRendition { private ISession session; private string objectId; /// /// Constructor. /// /// the session objec /// the object ID /// the stream ID /// the MIME type /// the length in bytes, if known /// the kind /// the title /// the thumbnail height /// the thumbnail width /// the ID of the stand-alone rendition document, if it exists public Rendition(ISession session, string objectId, string streamId, string mimeType, BigInteger? length, string kind, string title, BigInteger? height, BigInteger? width, string renditionDocumentId) { this.session = session; this.objectId = objectId; StreamId = streamId; MimeType = mimeType; Length = length; Kind = kind; Title = title; Height = height; Width = width; RenditionDocumentId = renditionDocumentId; } /// public virtual IDocument GetRenditionDocument() { return GetRenditionDocument(session.DefaultContext); } /// public virtual IDocument GetRenditionDocument(IOperationContext context) { if (RenditionDocumentId == null) { return null; } return session.GetObject(session.CreateObjectId(RenditionDocumentId), context) as IDocument; } /// public virtual IContentStream GetContentStream() { if (objectId == null || StreamId == null) { return null; } return session.Binding.GetObjectService().GetContentStream(session.RepositoryInfo.Id, objectId, StreamId, null, null, null); } } /// /// Content Stream Hash implementation. /// public class ContentStreamHash : IContentStreamHash { /// Algorithm MD5 public const string AlgorithmMD5 = "md5"; /// Algorithm sha-1 public const string AlgorithmSHA1 = "sha-1"; /// Algorithm sha-224 public const string AlgorithmSHA224 = "sha-224"; /// Algorithm sha-256 public const string AlgorithmSHA256 = "sha-256"; /// Algorithm sha-384 public const string AlgorithmSHA384 = "sha-384"; /// Algorithm sha-512 public const string AlgorithmSHA512 = "sha-512"; /// Algorithm sha-3 public const string AlgorithmSHA3 = "sha-3"; /// public string PropertyValue { get; protected set; } /// public string Algorithm { get; protected set; } /// public string Hash { get; protected set; } /// /// Constructor. /// /// a property value public ContentStreamHash(string propertyValue) { PropertyValue = propertyValue; if (propertyValue == null) { return; } string pv = propertyValue.Trim(); int algEnd = pv.IndexOf('}'); if (pv[0] != '{' || algEnd < 1) { return; } Algorithm = pv.Substring(1, algEnd - 1).ToLowerInvariant(); Hash = pv.Substring(algEnd + 1).Replace(" ", "").ToLowerInvariant(); } /// /// Constructor. /// /// the algorithm /// the hash string public ContentStreamHash(string algorithm, string hashStr) { if (algorithm == null || algorithm.Trim().Length == 0) { throw new ArgumentException("Algorithm must be set!", "algorithm"); } if (hashStr == null || hashStr.Trim().Length == 0) { throw new ArgumentException("Hash must be set!", "hashStr"); } Algorithm = algorithm.ToLowerInvariant(); Hash = hashStr.Replace(" ", "").ToLowerInvariant(); PropertyValue = "{" + Algorithm + "}" + Hash; } } /// /// Query Result implementation. /// public class QueryResult : IQueryResult { private IDictionary propertiesById; private IDictionary propertiesByQueryName; /// /// Constructor. /// /// the current session /// the query hit public QueryResult(ISession session, IObjectData objectData) { Initialize(session, objectData); } /// /// Initializes the query result object. /// /// the current session /// the query hit protected void Initialize(ISession session, IObjectData objectData) { if (objectData != null) { IObjectFactory of = session.ObjectFactory; // handle properties if (objectData.Properties != null) { Properties = new List(); propertiesById = new Dictionary(); propertiesByQueryName = new Dictionary(); IList queryProperties = of.ConvertQueryProperties(objectData.Properties); foreach (IPropertyData property in queryProperties) { Properties.Add(property); if (property.Id != null) { propertiesById[property.Id] = property; } if (property.QueryName != null) { propertiesByQueryName[property.QueryName] = property; } } } // handle allowable actions AllowableActions = objectData.AllowableActions; // handle relationships if (objectData.Relationships != null) { Relationships = new List(); foreach (IObjectData rod in objectData.Relationships) { IRelationship relationship = of.ConvertObject(rod, session.DefaultContext) as IRelationship; if (relationship != null) { Relationships.Add(relationship); } } } // handle renditions if (objectData.Renditions != null) { Renditions = new List(); foreach (IRenditionData rd in objectData.Renditions) { Renditions.Add(of.ConvertRendition(null, rd)); } } } } /// public IPropertyData this[string queryName] { get { if (queryName == null) { return null; } IPropertyData result; if (propertiesByQueryName.TryGetValue(queryName, out result)) { return result; } return null; } } /// public IList Properties { get; protected set; } /// public IPropertyData GetPropertyById(string propertyId) { if (propertyId == null) { return null; } IPropertyData result; if (propertiesById.TryGetValue(propertyId, out result)) { return result; } return null; } /// public object GetPropertyValueByQueryName(string queryName) { IPropertyData property = this[queryName]; if (property == null) { return null; } return property.FirstValue; } /// public object GetPropertyValueById(string propertyId) { IPropertyData property = GetPropertyById(propertyId); if (property == null) { return null; } return property.FirstValue; } /// public IList GetPropertyMultivalueByQueryName(string queryName) { IPropertyData property = this[queryName]; if (property == null) { return null; } return property.Values; } /// public IList GetPropertyMultivalueById(string propertyId) { IPropertyData property = GetPropertyById(propertyId); if (property == null) { return null; } return property.Values; } /// public IAllowableActions AllowableActions { get; protected set; } /// public IList Relationships { get; protected set; } /// public IList Renditions { get; protected set; } } /// /// Change Event implementation. /// public class ChangeEvent : ChangeEventInfo, IChangeEvent { /// public virtual string ObjectId { get; set; } /// public virtual IDictionary> Properties { get; set; } /// public virtual IList PolicyIds { get; set; } /// public virtual IAcl Acl { get; set; } } /// /// Change Events implementation. /// public class ChangeEvents : IChangeEvents { /// public virtual string LatestChangeLogToken { get; set; } /// public virtual IList ChangeEventList { get; set; } /// public virtual bool? HasMoreItems { get; set; } /// public virtual BigInteger? TotalNumItems { get; set; } } }