CMIS-792: Browser Binding: current state of write support

git-svn-id: https://svn.apache.org/repos/asf/chemistry/objectivecmis/branches/browser-binding@1590634 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomEntryWriter.m b/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomEntryWriter.m
index 4c1cab8..47b5db9 100644
--- a/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomEntryWriter.m
+++ b/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomEntryWriter.m
@@ -200,7 +200,7 @@
             }
             default:
             {
-                CMISLogDebug(@"Property type did not match: %d", propertyData.type);
+                CMISLogDebug(@"Property type did not match: %d", (int)propertyData.type);
                 break;
             }
         }
diff --git a/ObjectiveCMIS/Bindings/Browser/CMISBrowserConstants.h b/ObjectiveCMIS/Bindings/Browser/CMISBrowserConstants.h
index c7c1f57..f45f364 100644
--- a/ObjectiveCMIS/Bindings/Browser/CMISBrowserConstants.h
+++ b/ObjectiveCMIS/Bindings/Browser/CMISBrowserConstants.h
@@ -100,6 +100,7 @@
 extern NSString * const kCMISBrowserJSONMaxValue;
 extern NSString * const kCMISBrowserJSONPrecision;
 extern NSString * const kCMISBrowserJSONResolution;
+extern NSString * const kCMISBrowserJSONFailedToDeleteId;
 extern NSString * const kCMISBrowserJSONAcePrincipal;
 extern NSString * const kCMISBrowserJSONAcePrincipalId;
 extern NSString * const kCMISBrowserJSONAcePermissions;
@@ -161,6 +162,8 @@
 
 // Browser binding control
 extern NSString * const kCMISBrowserJSONControlCmisAction;
+extern NSString * const kCMISBrowserJSONControlPropertyId;
+extern NSString * const kCMISBrowserJSONControlPropertyValue;
 
 // Browser binding actions
 extern NSString * const kCMISBrowserJSONActionCreateType;
diff --git a/ObjectiveCMIS/Bindings/Browser/CMISBrowserConstants.m b/ObjectiveCMIS/Bindings/Browser/CMISBrowserConstants.m
index 866b90d..e14e5e9 100644
--- a/ObjectiveCMIS/Bindings/Browser/CMISBrowserConstants.m
+++ b/ObjectiveCMIS/Bindings/Browser/CMISBrowserConstants.m
@@ -113,6 +113,7 @@
 NSString * const kCMISBrowserJSONMaxValue = @"maxValue";
 NSString * const kCMISBrowserJSONPrecision = @"precision";
 NSString * const kCMISBrowserJSONResolution = @"resolution";
+NSString * const kCMISBrowserJSONFailedToDeleteId = @"ids";
 NSString * const kCMISBrowserJSONAcePrincipal = @"principal";
 NSString * const kCMISBrowserJSONAcePrincipalId = @"principalId";
 NSString * const kCMISBrowserJSONAcePermissions = @"permissions";
@@ -177,6 +178,8 @@
 
 // Browser binding control
 NSString * const kCMISBrowserJSONControlCmisAction = @"cmisaction";
+NSString * const kCMISBrowserJSONControlPropertyId = @"propertyId";
+NSString * const kCMISBrowserJSONControlPropertyValue = @"propertyValue";
 
 
 // Browser binding actions
diff --git a/ObjectiveCMIS/Bindings/Browser/CMISBrowserNavigationService.m b/ObjectiveCMIS/Bindings/Browser/CMISBrowserNavigationService.m
index 5d66904..94ac2f5 100644
--- a/ObjectiveCMIS/Bindings/Browser/CMISBrowserNavigationService.m
+++ b/ObjectiveCMIS/Bindings/Browser/CMISBrowserNavigationService.m
@@ -81,9 +81,35 @@
               includeRelativePathSegment:(BOOL)includeRelativePathSegment
                          completionBlock:(void (^)(NSArray *parents, NSError *error))completionBlock
 {
-    NSString * message = [NSString stringWithFormat:@"%s is not implemented yet", __PRETTY_FUNCTION__];
-    NSException *exception = [NSException exceptionWithName:NSInvalidArgumentException reason:message userInfo:nil];
-    @throw exception;
+    NSString *objectUrl = [self getObjectUrlObjectId:objectId selector:kCMISBrowserJSONSelectorParents];
+    objectUrl = [CMISURLUtil urlStringByAppendingParameter:kCMISParameterFilter value:filter urlString:objectUrl];
+    objectUrl = [CMISURLUtil urlStringByAppendingParameter:kCMISParameterIncludeAllowableActions boolValue:includeAllowableActions urlString:objectUrl];
+    objectUrl = [CMISURLUtil urlStringByAppendingParameter:kCMISParameterIncludeRelationships value:[CMISEnums stringForIncludeRelationShip:relationships] urlString:objectUrl];
+    objectUrl = [CMISURLUtil urlStringByAppendingParameter:kCMISParameterRenditionFilter value:renditionFilter urlString:objectUrl];
+    objectUrl = [CMISURLUtil urlStringByAppendingParameter:kCMISParameterRelativePathSegment boolValue:includeRelativePathSegment urlString:objectUrl];
+    objectUrl = [CMISURLUtil urlStringByAppendingParameter:kCMISBrowserJSONParameterSuccinct value:kCMISParameterValueTrue urlString:objectUrl];
+    
+    CMISRequest *cmisRequest = [[CMISRequest alloc] init];
+    
+    [self.bindingSession.networkProvider invokeGET:[NSURL URLWithString:objectUrl]
+                                           session:self.bindingSession
+                                       cmisRequest:cmisRequest
+                                   completionBlock:^(CMISHttpResponse *httpResponse, NSError *error) {
+                                       if (httpResponse.statusCode == 200 && httpResponse.data) {
+                                           CMISTypeCache *typeCache = [[CMISTypeCache alloc] initWithRepositoryId:self.bindingSession.repositoryId bindingService:self];
+                                           [CMISBrowserUtil objectParents:httpResponse.data typeCache:typeCache completionBlock:^(NSArray *objectParents, NSError *error) {
+                                               if (error) {
+                                                   completionBlock(nil, error);
+                                               } else {
+                                                   completionBlock(objectParents, nil);
+                                               }
+                                           }];
+                                       } else {
+                                           completionBlock(nil, error);
+                                       }
+                                   }];
+    
+    return cmisRequest;
 }
 
 @end
diff --git a/ObjectiveCMIS/Bindings/Browser/CMISBrowserObjectService.m b/ObjectiveCMIS/Bindings/Browser/CMISBrowserObjectService.m
index 070e593..05907de 100644
--- a/ObjectiveCMIS/Bindings/Browser/CMISBrowserObjectService.m
+++ b/ObjectiveCMIS/Bindings/Browser/CMISBrowserObjectService.m
@@ -28,6 +28,7 @@
 #import "CMISErrors.h"
 #import "CMISLog.h"
 #import "CMISFormDataWriter.h"
+#import "CMISStringInOutParameter.h"
 
 @implementation CMISBrowserObjectService
 
@@ -211,9 +212,47 @@
                           changeToken:(CMISStringInOutParameter *)changeTokenParam
                       completionBlock:(void (^)(NSError *error))completionBlock
 {
-    NSString * message = [NSString stringWithFormat:@"%s is not implemented yet", __PRETTY_FUNCTION__];
-    NSException *exception = [NSException exceptionWithName:NSInvalidArgumentException reason:message userInfo:nil];
-    @throw exception;
+    // we need an object id
+    if ((objectIdParam.inParameter == nil) || (objectIdParam.inParameter.length == 0)) {
+        completionBlock([CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument
+                                        detailedDescription:@"Object id must be set!"]);
+    }
+    
+    // build URL
+    NSString *objectUrl = [self getObjectUrlObjectId:objectIdParam.inParameter];
+    
+    // prepare form data
+    CMISFormDataWriter *formData = [[CMISFormDataWriter alloc] initWithAction:kCMISBrowserJSONActionDeleteContent];
+    [formData addParameter:kCMISParameterChangeToken value:changeTokenParam.inParameter];
+    [formData addSuccinctFlag:true];
+    
+    
+    CMISRequest *cmisRequest = [[CMISRequest alloc] init];
+    
+    // send
+    [self.bindingSession.networkProvider invokePOST:[NSURL URLWithString:objectUrl]
+                                            session:self.bindingSession
+                                               body:formData.body
+                                            headers:formData.headers
+                                        cmisRequest:cmisRequest
+                                    completionBlock:^(CMISHttpResponse *httpResponse, NSError *error) {
+                                        if ((httpResponse.statusCode == 200 || httpResponse.statusCode == 201) && httpResponse.data) {
+                                            CMISTypeCache *typeCache = [[CMISTypeCache alloc] initWithRepositoryId:self.bindingSession.repositoryId bindingService:self];
+                                            [CMISBrowserUtil objectDataFromJSONData:httpResponse.data typeCache:typeCache completionBlock:^(CMISObjectData *objectData, NSError *error) {
+                                                if (error) {
+                                                    completionBlock(error);
+                                                } else {
+                                                    objectIdParam.outParameter = objectData.identifier;
+                                                    changeTokenParam.outParameter = objectData.properties.propertiesDictionary[kCMISPropertyChangeToken];
+                                                    
+                                                    completionBlock(nil);
+                                                }
+                                            }];
+                                        } else {
+                                            completionBlock(error);
+                                        }
+                                    }];
+    return cmisRequest;
 }
 
 - (CMISRequest*)changeContentOfObject:(CMISStringInOutParameter *)objectIdParam
@@ -224,9 +263,30 @@
                       completionBlock:(void (^)(NSError *error))completionBlock
                         progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock
 {
-    NSString * message = [NSString stringWithFormat:@"%s is not implemented yet", __PRETTY_FUNCTION__];
-    NSException *exception = [NSException exceptionWithName:NSInvalidArgumentException reason:message userInfo:nil];
-    @throw exception;
+    NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:filePath];
+    if (inputStream == nil) {
+        CMISLogError(@"Could not find file %@", filePath);
+        if (completionBlock) {
+            completionBlock([CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:nil]);
+        }
+        return nil;
+    }
+    
+    NSError *fileError = nil;
+    unsigned long long fileSize = [CMISFileUtil fileSizeForFileAtPath:filePath error:&fileError];
+    if (fileError) {
+        CMISLogError(@"Could not determine size of file %@: %@", filePath, [fileError description]);
+    }
+    
+    return [self changeContentOfObject:objectIdParam
+                toContentOfInputStream:inputStream
+                         bytesExpected:fileSize
+                              filename:[filePath lastPathComponent]
+                              mimeType:mimeType
+                     overwriteExisting:overwrite
+                           changeToken:changeTokenParam
+                       completionBlock:completionBlock
+                         progressBlock:progressBlock];
 }
 
 - (CMISRequest*)changeContentOfObject:(CMISStringInOutParameter *)objectId
@@ -322,9 +382,41 @@
                                 properties:(CMISProperties *)properties
                            completionBlock:(void (^)(NSString *objectId, NSError *error))completionBlock
 {
-    NSString * message = [NSString stringWithFormat:@"%s is not implemented yet", __PRETTY_FUNCTION__];
-    NSException *exception = [NSException exceptionWithName:NSInvalidArgumentException reason:message userInfo:nil];
-    @throw exception;
+    // build URL
+    NSString *folderObjectUrl = [self getObjectUrlObjectId:folderObjectId];
+    
+    // prepare form data
+    CMISFormDataWriter *formData = [[CMISFormDataWriter alloc] initWithAction:kCMISBrowserJSONActionCreateFolder];
+    [formData addPropertiesParameters:properties];
+    // TODO [formData addPoliciesParameters:policies];
+    // TODO [formData addAddAcesParameters:addAces];
+    // TODO [formData addRemoveAcesParameters:removeAces];
+    [formData addSuccinctFlag:true];
+    
+    
+    CMISRequest *cmisRequest = [[CMISRequest alloc] init];
+    
+    // send
+    [self.bindingSession.networkProvider invokePOST:[NSURL URLWithString:folderObjectUrl]
+                                            session:self.bindingSession
+                                               body:formData.body
+                                            headers:formData.headers
+                                        cmisRequest:cmisRequest
+                                    completionBlock:^(CMISHttpResponse *httpResponse, NSError *error) {
+                                        if ((httpResponse.statusCode == 200 || httpResponse.statusCode == 201) && httpResponse.data) {
+                                            CMISTypeCache *typeCache = [[CMISTypeCache alloc] initWithRepositoryId:self.bindingSession.repositoryId bindingService:self];
+                                            [CMISBrowserUtil objectDataFromJSONData:httpResponse.data typeCache:typeCache completionBlock:^(CMISObjectData *objectData, NSError *error) {
+                                                if (error) {
+                                                    completionBlock(nil, error);
+                                                } else {
+                                                    completionBlock(objectData.identifier, nil);
+                                                }
+                                            }];
+                                        } else {
+                                            completionBlock(nil, error);
+                                        }
+                                    }];
+    return cmisRequest;
 }
 
 - (CMISRequest*)moveObject:(NSString *)objectId
@@ -332,9 +424,45 @@
                   toFolder:(NSString *)targetFolderId
            completionBlock:(void (^)(CMISObjectData *objectData, NSError *error))completionBlock
 {
-    NSString * message = [NSString stringWithFormat:@"%s is not implemented yet", __PRETTY_FUNCTION__];
-    NSException *exception = [NSException exceptionWithName:NSInvalidArgumentException reason:message userInfo:nil];
-    @throw exception;
+    // we need an object id
+    if ((objectId == nil) || (objectId.length == 0)) {
+        completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument
+                                        detailedDescription:@"Object id must be set!"]);
+    }
+    
+    // build URL
+    NSString *objectUrl = [self getObjectUrlObjectId:objectId];
+    
+    // prepare form data
+    CMISFormDataWriter *formData = [[CMISFormDataWriter alloc] initWithAction:kCMISBrowserJSONActionMove];
+    [formData addParameter:kCMISParameterTargetFolderId value:targetFolderId];
+    [formData addParameter:kCMISParameterSourceFolderId value:sourceFolderId];
+    [formData addSuccinctFlag:true];
+    
+    
+    CMISRequest *cmisRequest = [[CMISRequest alloc] init];
+    
+    // send
+    [self.bindingSession.networkProvider invokePOST:[NSURL URLWithString:objectUrl]
+                                            session:self.bindingSession
+                                               body:formData.body
+                                            headers:formData.headers
+                                        cmisRequest:cmisRequest
+                                    completionBlock:^(CMISHttpResponse *httpResponse, NSError *error) {
+                                        if ((httpResponse.statusCode == 200 || httpResponse.statusCode == 201) && httpResponse.data) {
+                                            CMISTypeCache *typeCache = [[CMISTypeCache alloc] initWithRepositoryId:self.bindingSession.repositoryId bindingService:self];
+                                            [CMISBrowserUtil objectDataFromJSONData:httpResponse.data typeCache:typeCache completionBlock:^(CMISObjectData *objectData, NSError *error) {
+                                                if (error) {
+                                                    completionBlock(nil, error);
+                                                } else {
+                                                    completionBlock(objectData, nil);
+                                                }
+                                            }];
+                                        } else {
+                                            completionBlock(nil, error);
+                                        }
+                                    }];
+    return cmisRequest;
 }
 
 - (CMISRequest*)deleteTree:(NSString *)folderObjectId
@@ -343,9 +471,41 @@
          continueOnFailure:(BOOL)continueOnFailure
            completionBlock:(void (^)(NSArray *failedObjects, NSError *error))completionBlock
 {
-    NSString * message = [NSString stringWithFormat:@"%s is not implemented yet", __PRETTY_FUNCTION__];
-    NSException *exception = [NSException exceptionWithName:NSInvalidArgumentException reason:message userInfo:nil];
-    @throw exception;
+    // build URL
+    NSString *folderObjectUrl = [self getObjectUrlObjectId:folderObjectId];
+    
+    CMISFormDataWriter *formData = [[CMISFormDataWriter alloc] initWithAction:kCMISBrowserJSONActionDeleteTree];
+    [formData addParameter:kCMISParameterAllVersions boolValue:allVersions];
+    [formData addParameter:kCMISParameterUnfileObjects value:[CMISEnums stringForUnfileObject:unfileObjects]];
+    [formData addParameter:kCMISParameterContinueOnFailure boolValue:continueOnFailure];
+    
+    CMISRequest *cmisRequest = [[CMISRequest alloc] init];
+    
+    // send
+    [self.bindingSession.networkProvider invokePOST:[NSURL URLWithString:folderObjectUrl]
+                                            session:self.bindingSession
+                                               body:formData.body
+                                            headers:formData.headers
+                                        cmisRequest:cmisRequest
+                                    completionBlock:^(CMISHttpResponse *httpResponse, NSError *error) {
+                                        if ((httpResponse.statusCode == 200 || httpResponse.statusCode == 201) && httpResponse.data) {
+                                            NSError *error = nil;
+                                            if(httpResponse.data.length > 0) {
+                                                NSArray *failedToDeleteIds = [CMISBrowserUtil failedToDeleteObjectsFromJSONData:httpResponse.data error:&error];
+                                                if (error) {
+                                                    completionBlock(nil, error);
+                                                } else {
+                                                    completionBlock(failedToDeleteIds, nil);
+                                                }
+                                            } else {
+                                                completionBlock([NSArray array], nil);
+                                            }
+                                        } else {
+                                            completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeConnection]);
+                                        }
+                                    }];
+    
+    return cmisRequest;
 }
 
 - (CMISRequest*)updatePropertiesForObject:(CMISStringInOutParameter *)objectIdParam
@@ -353,9 +513,48 @@
                               changeToken:(CMISStringInOutParameter *)changeTokenParam
                           completionBlock:(void (^)(NSError *error))completionBlock
 {
-    NSString * message = [NSString stringWithFormat:@"%s is not implemented yet", __PRETTY_FUNCTION__];
-    NSException *exception = [NSException exceptionWithName:NSInvalidArgumentException reason:message userInfo:nil];
-    @throw exception;
+    // we need an object id
+    if ((objectIdParam.inParameter == nil) || (objectIdParam.inParameter.length == 0)) {
+        completionBlock([CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument
+                                        detailedDescription:@"Object id must be set!"]);
+    }
+    
+    // build URL
+    NSString *objectUrl = [self getObjectUrlObjectId:objectIdParam.inParameter];
+    
+    // prepare form data
+    CMISFormDataWriter *formData = [[CMISFormDataWriter alloc] initWithAction:kCMISBrowserJSONActionUpdateProperties];
+    [formData addPropertiesParameters:properties];
+    [formData addParameter:kCMISParameterChangeToken value:changeTokenParam.inParameter];
+    [formData addSuccinctFlag:true];
+    
+    
+    CMISRequest *cmisRequest = [[CMISRequest alloc] init];
+    
+    // send
+    [self.bindingSession.networkProvider invokePOST:[NSURL URLWithString:objectUrl]
+                                            session:self.bindingSession
+                                               body:formData.body
+                                            headers:formData.headers
+                                        cmisRequest:cmisRequest
+                                    completionBlock:^(CMISHttpResponse *httpResponse, NSError *error) {
+                                        if ((httpResponse.statusCode == 200 || httpResponse.statusCode == 201) && httpResponse.data) {
+                                            CMISTypeCache *typeCache = [[CMISTypeCache alloc] initWithRepositoryId:self.bindingSession.repositoryId bindingService:self];
+                                            [CMISBrowserUtil objectDataFromJSONData:httpResponse.data typeCache:typeCache completionBlock:^(CMISObjectData *objectData, NSError *error) {
+                                                if (error) {
+                                                    completionBlock(error);
+                                                } else {
+                                                    objectIdParam.outParameter = objectData.identifier;
+                                                    changeTokenParam.outParameter = objectData.properties.propertiesDictionary[kCMISPropertyChangeToken];
+                                                    
+                                                    completionBlock(nil);
+                                                }
+                                            }];
+                                        } else {
+                                            completionBlock(error);
+                                        }
+                                    }];
+    return cmisRequest;
 }
 
 - (CMISRequest*)retrieveRenditions:(NSString *)objectId
diff --git a/ObjectiveCMIS/Bindings/Browser/CMISBrowserUtil.h b/ObjectiveCMIS/Bindings/Browser/CMISBrowserUtil.h
index e9aa63c..d2da4ed 100644
--- a/ObjectiveCMIS/Bindings/Browser/CMISBrowserUtil.h
+++ b/ObjectiveCMIS/Bindings/Browser/CMISBrowserUtil.h
@@ -51,4 +51,14 @@
  */
 + (NSArray *)renditionsFromJSONData:(NSData *)jsonData error:(NSError **)outError;
 
+/**
+ Returns an array of NSString objects that failed to be deleted, parsed from the given JSON data.
+ */
++ (NSArray *)failedToDeleteObjectsFromJSONData:(NSData *)jsonData error:(NSError **)outError;
+
+/**
+ Returns all object parents as an array of CMISObjectData objects, parsed from the given JSON data.
+ */
++ (void)objectParents:(NSData *)jsonData typeCache:(CMISTypeCache *)typeCache completionBlock:(void(^)(NSArray *objectParents, NSError *error))completionBlock;
+
 @end
diff --git a/ObjectiveCMIS/Bindings/Browser/CMISBrowserUtil.m b/ObjectiveCMIS/Bindings/Browser/CMISBrowserUtil.m
index 7228a0b..5ec21b1 100644
--- a/ObjectiveCMIS/Bindings/Browser/CMISBrowserUtil.m
+++ b/ObjectiveCMIS/Bindings/Browser/CMISBrowserUtil.m
@@ -89,6 +89,9 @@
             
             [repositories setObject:repoInfo forKey:repoInfo.identifier];
         }
+    } else {
+        if (outError != NULL) *outError = [CMISErrors cmisError:serialisationError cmisErrorCode:kCMISErrorCodeRuntime];
+        return nil;
     }
 
     return repositories;
@@ -271,11 +274,55 @@
     if (!serialisationError) {
         // parse the json into a CMISObjectData object
         renditions = [CMISBrowserUtil renditionsFromArray:jsonDictionary];
+    } else {
+        if (outError != NULL) *outError = [CMISErrors cmisError:serialisationError cmisErrorCode:kCMISErrorCodeRuntime];
+        return nil;
     }
     
     return renditions;
 }
 
++ (NSArray *)failedToDeleteObjectsFromJSONData:(NSData *)jsonData error:(NSError **)outError
+{
+    // TODO: error handling i.e. if jsonData is nil, also handle outError being nil
+    
+    // parse the JSON response
+    NSError *serialisationError = nil;
+    id jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&serialisationError];
+    
+    if (!serialisationError) {
+        NSMutableArray *ids = [[NSMutableArray alloc] init];
+        NSArray *jsonIds = [jsonDictionary cmis_objectForKeyNotNull:kCMISBrowserJSONFailedToDeleteId];
+        
+        if (jsonIds) {
+            for (NSObject *obj in jsonIds) {
+                [ids addObject:obj.description]; //obj can't be nil as it came out of an array
+            }
+        }
+        
+        return ids;
+    } else {
+        if (outError != NULL) *outError = [CMISErrors cmisError:serialisationError cmisErrorCode:kCMISErrorCodeRuntime];
+        return nil;
+    }
+}
+
++ (void)objectParents:(NSData *)jsonData typeCache:(CMISTypeCache *)typeCache completionBlock:(void(^)(NSArray *objectParents, NSError *error))completionBlock
+{
+    // TODO: error handling i.e. if jsonData is nil, also handle outError being nil
+    
+    // parse the JSON response
+    NSError *serialisationError = nil;
+    id jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&serialisationError];
+    
+    if (!serialisationError) {
+        [self convertObjects:jsonDictionary typeCache:typeCache completionBlock:completionBlock];
+    } else {
+        completionBlock(nil, [CMISErrors cmisError:serialisationError cmisErrorCode:kCMISErrorCodeRuntime]);
+        return;
+    }
+}
+
 #pragma mark -
 #pragma mark Private helper methods
 
diff --git a/ObjectiveCMIS/Bindings/Browser/CMISBrowserVersioningService.m b/ObjectiveCMIS/Bindings/Browser/CMISBrowserVersioningService.m
index 7909155..df6dea2 100644
--- a/ObjectiveCMIS/Bindings/Browser/CMISBrowserVersioningService.m
+++ b/ObjectiveCMIS/Bindings/Browser/CMISBrowserVersioningService.m
@@ -24,6 +24,10 @@
 #import "CMISBrowserUtil.h"
 #import "CMISURLUtil.h"
 #import "CMISConstants.h"
+#import "CMISErrors.h"
+#import "CMISFormDataWriter.h"
+#import "CMISFileUtil.h"
+#import "CMISLog.h"
 
 @implementation CMISBrowserVersioningService
 
@@ -114,9 +118,43 @@
 - (CMISRequest*)checkOut:(NSString *)objectId
          completionBlock:(void (^)(CMISObjectData *objectData, NSError *error))completionBlock
 {
-    NSString * message = [NSString stringWithFormat:@"%s is not implemented yet", __PRETTY_FUNCTION__];
-    NSException *exception = [NSException exceptionWithName:NSInvalidArgumentException reason:message userInfo:nil];
-    @throw exception;
+    // we need an object id
+    if ((objectId == nil) || (objectId.length == 0)) {
+        completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument
+                                        detailedDescription:@"Object id must be set!"]);
+    }
+    
+    // build URL
+    NSString *objectUrl = [self getObjectUrlObjectId:objectId];
+    
+    // prepare form data
+    CMISFormDataWriter *formData = [[CMISFormDataWriter alloc] initWithAction:kCMISBrowserJSONActionCheckOut];
+    [formData addSuccinctFlag:true];
+    
+    
+    CMISRequest *cmisRequest = [[CMISRequest alloc] init];
+    
+    // send
+    [self.bindingSession.networkProvider invokePOST:[NSURL URLWithString:objectUrl]
+                                            session:self.bindingSession
+                                               body:formData.body
+                                            headers:formData.headers
+                                        cmisRequest:cmisRequest
+                                    completionBlock:^(CMISHttpResponse *httpResponse, NSError *error) {
+                                        if ((httpResponse.statusCode == 200 || httpResponse.statusCode == 201) && httpResponse.data) {
+                                            CMISTypeCache *typeCache = [[CMISTypeCache alloc] initWithRepositoryId:self.bindingSession.repositoryId bindingService:self];
+                                            [CMISBrowserUtil objectDataFromJSONData:httpResponse.data typeCache:typeCache completionBlock:^(CMISObjectData *objectData, NSError *error) {
+                                                if (error) {
+                                                    completionBlock(nil, error);
+                                                } else {
+                                                    completionBlock(objectData, nil);
+                                                }
+                                            }];
+                                        } else {
+                                            completionBlock(nil, error);
+                                        }
+                                    }];
+    return cmisRequest;
 }
 
 /**
@@ -128,9 +166,35 @@
 - (CMISRequest*)cancelCheckOut:(NSString *)objectId
                completionBlock:(void (^)(BOOL checkOutCancelled, NSError *error))completionBlock
 {
-    NSString * message = [NSString stringWithFormat:@"%s is not implemented yet", __PRETTY_FUNCTION__];
-    NSException *exception = [NSException exceptionWithName:NSInvalidArgumentException reason:message userInfo:nil];
-    @throw exception;
+    // we need an object id
+    if ((objectId == nil) || (objectId.length == 0)) {
+        completionBlock(NO, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument
+                                             detailedDescription:@"Object id must be set!"]);
+    }
+    
+    // build URL
+    NSString *objectUrl = [self getObjectUrlObjectId:objectId];
+    
+    // prepare form data
+    CMISFormDataWriter *formData = [[CMISFormDataWriter alloc] initWithAction:kCMISBrowserJSONActionCancelCheckOut];
+    
+    
+    CMISRequest *cmisRequest = [[CMISRequest alloc] init];
+    
+    // send
+    [self.bindingSession.networkProvider invokePOST:[NSURL URLWithString:objectUrl]
+                                            session:self.bindingSession
+                                               body:formData.body
+                                            headers:formData.headers
+                                        cmisRequest:cmisRequest
+                                    completionBlock:^(CMISHttpResponse *httpResponse, NSError *error) {
+                                        if ((httpResponse.statusCode == 200 || httpResponse.statusCode == 201) && httpResponse.data) {
+                                            completionBlock(YES, nil);
+                                        } else {
+                                            completionBlock(NO, error);
+                                        }
+                                    }];
+    return cmisRequest;
 }
 
 /**
@@ -154,9 +218,30 @@
         completionBlock:(void (^)(CMISObjectData *objectData, NSError *error))completionBlock
           progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock
 {
-    NSString * message = [NSString stringWithFormat:@"%s is not implemented yet", __PRETTY_FUNCTION__];
-    NSException *exception = [NSException exceptionWithName:NSInvalidArgumentException reason:message userInfo:nil];
-    @throw exception;
+    NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:filePath];
+    if (inputStream == nil) {
+        CMISLogError(@"Could not find file %@", filePath);
+        if (completionBlock) {
+            completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:nil]);
+        }
+        return nil;
+    }
+    
+    NSError *fileError = nil;
+    unsigned long long fileSize = [CMISFileUtil fileSizeForFileAtPath:filePath error:&fileError];
+    if (fileError) {
+        CMISLogError(@"Could not determine size of file %@: %@", filePath, [fileError description]);
+    }
+    
+    return [self checkIn:objectId
+          asMajorVersion:asMajorVersion
+             inputStream:inputStream
+           bytesExpected:fileSize
+                mimeType:mimeType
+              properties:properties
+          checkinComment:checkinComment
+         completionBlock:completionBlock
+           progressBlock:progressBlock];
 }
 
 /**
diff --git a/ObjectiveCMIS/Bindings/Browser/CMISFormDataWriter.h b/ObjectiveCMIS/Bindings/Browser/CMISFormDataWriter.h
index 8dc5f8e..a20d0ab 100644
--- a/ObjectiveCMIS/Bindings/Browser/CMISFormDataWriter.h
+++ b/ObjectiveCMIS/Bindings/Browser/CMISFormDataWriter.h
@@ -18,6 +18,7 @@
  */
 
 #import <Foundation/Foundation.h>
+#import "CMISProperties.h"
 
 @interface CMISFormDataWriter : NSObject
 
@@ -27,6 +28,10 @@
 
 - (void)addParameter:(NSString *)name boolValue:(BOOL)value;
 
+- (void)addPropertiesParameters:(CMISProperties *)properties;
+
+- (void)addSuccinctFlag:(BOOL)succinct;
+
 - (NSDictionary *)headers;
 
 - (NSData *)body;
diff --git a/ObjectiveCMIS/Bindings/Browser/CMISFormDataWriter.m b/ObjectiveCMIS/Bindings/Browser/CMISFormDataWriter.m
index faf883a..80e12bb 100644
--- a/ObjectiveCMIS/Bindings/Browser/CMISFormDataWriter.m
+++ b/ObjectiveCMIS/Bindings/Browser/CMISFormDataWriter.m
@@ -20,6 +20,8 @@
 #import "CMISFormDataWriter.h"
 #import "CMISConstants.h"
 #import "CMISBrowserConstants.h"
+#import "CMISEnums.h"
+#import "CMISLog.h"
 
 NSString * const kCMISFormDataContentTypeUrlEncoded = @"application/x-www-form-urlencoded;charset=utf-8";
 
@@ -62,6 +64,66 @@
     [self addParameter:name value:(value? kCMISParameterValueTrue : kCMISParameterValueFalse)];
 }
 
+- (void)addSuccinctFlag:(BOOL)succinct
+{
+    if (succinct) {
+        [self addParameter:kCMISBrowserJSONParameterSuccinct value:kCMISParameterValueTrue];
+    }
+}
+
+- (void)addPropertiesParameters:(CMISProperties *)properties
+{
+    if (!properties) {
+        return;
+    }
+    
+    int idx = 0;
+    
+    for (CMISPropertyData *prop in properties.propertyList) {
+        
+        NSString *idxStr = [NSString stringWithFormat:@"[%d]", idx];
+        
+        
+        [self addParameter:[NSString stringWithFormat:@"%@%@", kCMISBrowserJSONControlPropertyId, idxStr] value:prop.identifier];
+        
+        if (prop.values && prop.values.count > 0) {
+            if (prop.values.count == 1) {
+                NSString *value = [self convertPropertyValue:prop.firstValue forPropertyType:prop.type];
+                [self addParameter:[NSString stringWithFormat:@"%@%@", kCMISBrowserJSONControlPropertyValue, idxStr] value:value];
+            } else {
+                int vidx = 0;
+                for (id obj in prop.values) {
+                    NSString *vidxStr = [NSString stringWithFormat:@"[%d]", vidx];
+                    NSString *value = [self convertPropertyValue:obj forPropertyType:prop.type];
+                    [self addParameter:[NSString stringWithFormat:@"%@%@%@", kCMISBrowserJSONControlPropertyValue, idxStr, vidxStr] value:value];
+                    vidx++;
+                }
+            }
+        }
+        
+        idx++;
+    }
+}
+
+// TODO should this method be part of CMISPropertyData class (as class method?)
+- (NSString *)convertPropertyValue:(id)value forPropertyType:(CMISPropertyType)type
+{
+    if (!value) {
+        return nil;
+    }
+    
+    if (type == CMISPropertyTypeBoolean) {
+        return [value boolValue] ? kCMISParameterValueTrue : kCMISParameterValueFalse;
+    } else if (type == CMISPropertyTypeDateTime) {
+        if ([value isKindOfClass:NSDate.class]) {
+            return [NSNumber numberWithDouble:[(NSDate *)value timeIntervalSinceReferenceDate]].description;
+        } else {
+            CMISLogWarning(@"value is not a date!");
+        }
+    }
+    return value;
+}
+
 - (NSDictionary *)headers
 {
     return @{@"Content-Type" : kCMISFormDataContentTypeUrlEncoded};
diff --git a/ObjectiveCMIS/Bindings/CMISObjectService.h b/ObjectiveCMIS/Bindings/CMISObjectService.h
index bc7bfe2..7aa8746 100644
--- a/ObjectiveCMIS/Bindings/CMISObjectService.h
+++ b/ObjectiveCMIS/Bindings/CMISObjectService.h
@@ -215,7 +215,7 @@
 /**
  * Deletes the given folder and all of its subfolder and files
  *
- * Returns a list of objects which failed to be deleted.
+ * Returns a list of object ids which failed to be deleted.
  * completionBlock returns array of failed objects if any. NSError will be nil if successful
  */
 - (CMISRequest*)deleteTree:(NSString *)folderObjectId
diff --git a/ObjectiveCMIS/Common/CMISConstants.h b/ObjectiveCMIS/Common/CMISConstants.h
index c27f0d4..3598125 100644
--- a/ObjectiveCMIS/Common/CMISConstants.h
+++ b/ObjectiveCMIS/Common/CMISConstants.h
@@ -116,6 +116,7 @@
 extern NSString * const kCMISParameterCheckin;
 extern NSString * const kCMISParameterCheckinComment;
 extern NSString * const kCMISParameterSourceFolderId;
+extern NSString * const kCMISParameterTargetFolderId;
 extern NSString * const kCMISParameterReturnVersion;
 extern NSString * const kCMISParameterTypeId;
 extern NSString * const kCMISParameterStatement;
diff --git a/ObjectiveCMIS/Common/CMISConstants.m b/ObjectiveCMIS/Common/CMISConstants.m
index 263ef19..1f5ffcd 100644
--- a/ObjectiveCMIS/Common/CMISConstants.m
+++ b/ObjectiveCMIS/Common/CMISConstants.m
@@ -124,6 +124,7 @@
 NSString * const kCMISParameterCheckin = @"checkin";
 NSString * const kCMISParameterCheckinComment = @"checkinComment";
 NSString * const kCMISParameterSourceFolderId = @"sourceFolderId";
+NSString * const kCMISParameterTargetFolderId = @"targetFolderId";
 NSString * const kCMISParameterReturnVersion = @"returnVersion";
 NSString * const kCMISParameterTypeId = @"typeId";
 NSString * const kCMISParameterStatement = @"statement";
diff --git a/ObjectiveCMIS/Common/CMISEnums.m b/ObjectiveCMIS/Common/CMISEnums.m
index 69c9671..b651ecf 100644
--- a/ObjectiveCMIS/Common/CMISEnums.m
+++ b/ObjectiveCMIS/Common/CMISEnums.m
@@ -41,7 +41,7 @@
             includeRelationShipString = @"both";
             break;
         default:
-            CMISLogError(@"Invalid enum type %d", includeRelationship);
+            CMISLogError(@"Invalid enum type %d", (int)includeRelationship);
             break;
     }
     return includeRelationShipString;
@@ -61,7 +61,7 @@
             unfileObjectString = @"delete";
             break;
         default:
-            CMISLogError(@"Inavlid enum type %d", unfileObject);
+            CMISLogError(@"Inavlid enum type %d", (int)unfileObject);
             break;
     }
     return unfileObjectString;
diff --git a/ObjectiveCMIS/Utils/CMISDefaultNetworkProvider.m b/ObjectiveCMIS/Utils/CMISDefaultNetworkProvider.m
index 0834fec..aa52c02 100644
--- a/ObjectiveCMIS/Utils/CMISDefaultNetworkProvider.m
+++ b/ObjectiveCMIS/Utils/CMISDefaultNetworkProvider.m
@@ -302,7 +302,7 @@
             httpMethod = @"PUT";
             break;
         default:
-            CMISLogError(@"Invalid http request method: %d", httpRequestMethod);
+            CMISLogError(@"Invalid http request method: %d", (int)httpRequestMethod);
             return nil;
     }
     
diff --git a/ObjectiveCMIS/Utils/CMISHttpRequest.m b/ObjectiveCMIS/Utils/CMISHttpRequest.m
index 35b246f..10c84bc 100644
--- a/ObjectiveCMIS/Utils/CMISHttpRequest.m
+++ b/ObjectiveCMIS/Utils/CMISHttpRequest.m
@@ -194,7 +194,7 @@
 - (BOOL)checkStatusCodeForResponse:(CMISHttpResponse *)response httpRequestMethod:(CMISHttpRequestMethod)httpRequestMethod error:(NSError **)error
 {
     if ([CMISLog sharedInstance].logLevel == CMISLogLevelTrace) {
-        CMISLogTrace(@"Response status code: %d", response.statusCode);
+        CMISLogTrace(@"Response status code: %ld", (long)response.statusCode);
         CMISLogTrace(@"Response body: %@", [[NSString alloc] initWithData:response.data encoding:NSUTF8StringEncoding]);
     }
     
diff --git a/ObjectiveCMISTests/ObjectiveCMISTests.m b/ObjectiveCMISTests/ObjectiveCMISTests.m
index 0ab7ce8..6fbb9ce 100644
--- a/ObjectiveCMISTests/ObjectiveCMISTests.m
+++ b/ObjectiveCMISTests/ObjectiveCMISTests.m
@@ -743,6 +743,7 @@
                                                  continueOnFailure:YES
                                                    completionBlock:^(NSArray *failedObjects, NSError *error) {
                                                        XCTAssertNil(error, @"Error while move test folders and document: %@", [error description]);
+                                                       XCTAssertTrue(failedObjects.count == 0, @"some objects could not be deleted");
                                                        
                                                        self.testCompleted = YES;
                                                    }];