Sync is saving the last completed date.
authorMiltiadis Vasilakis <mvasilak@gmail.com>
Fri, 21 Oct 2011 17:56:56 +0000 (20:56 +0300)
committerMiltiadis Vasilakis <mvasilak@gmail.com>
Fri, 21 Oct 2011 17:56:56 +0000 (20:56 +0300)
Remote objects that are deleted in sync are moved to trash instead.
Dates shown in descriptive format.

pithos-macos.xcodeproj/project.pbxproj
pithos-macos/LastCompletedSyncTransformer.h [new file with mode: 0644]
pithos-macos/LastCompletedSyncTransformer.m [new file with mode: 0644]
pithos-macos/LastModifiedDateTransformer.m
pithos-macos/PithosSyncDaemon.h
pithos-macos/PithosSyncDaemon.m
pithos-macos/en.lproj/MainMenu.xib
pithos-macos/pithos_macosAppDelegate.h
pithos-macos/pithos_macosAppDelegate.m

index e45a1c5..b9787d3 100644 (file)
@@ -52,6 +52,7 @@
                615615DD140CECDB00017BD4 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 615615DC140CECDB00017BD4 /* libz.dylib */; };
                615A444E140F8A5700308614 /* MetadataKeyTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 615A444D140F8A5700308614 /* MetadataKeyTransformer.m */; };
                615A4451140F8A7F00308614 /* MetadataKeyFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 615A4450140F8A7F00308614 /* MetadataKeyFormatter.m */; };
+               6164B2DD1450E55C00D4C1AB /* LastCompletedSyncTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6164B2DC1450E55C00D4C1AB /* LastCompletedSyncTransformer.m */; };
                616FC0AF13F91BA400140A33 /* PithosObjectNodeInfoController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 616FC0AE13F91BA400140A33 /* PithosObjectNodeInfoController.xib */; };
                616FC0B313F97D0800140A33 /* PithosNodeInfoController.m in Sources */ = {isa = PBXBuildFile; fileRef = 616FC0B213F97D0800140A33 /* PithosNodeInfoController.m */; };
                617460F1140BE45C00D333A1 /* BytesExtendedSizeTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 617460F0140BE45C00D333A1 /* BytesExtendedSizeTransformer.m */; };
                615A444D140F8A5700308614 /* MetadataKeyTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MetadataKeyTransformer.m; path = "pithos-macos/MetadataKeyTransformer.m"; sourceTree = "<group>"; };
                615A444F140F8A7F00308614 /* MetadataKeyFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MetadataKeyFormatter.h; path = "pithos-macos/MetadataKeyFormatter.h"; sourceTree = "<group>"; };
                615A4450140F8A7F00308614 /* MetadataKeyFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MetadataKeyFormatter.m; path = "pithos-macos/MetadataKeyFormatter.m"; sourceTree = "<group>"; };
+               6164B2DB1450E55C00D4C1AB /* LastCompletedSyncTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LastCompletedSyncTransformer.h; path = "pithos-macos/LastCompletedSyncTransformer.h"; sourceTree = "<group>"; };
+               6164B2DC1450E55C00D4C1AB /* LastCompletedSyncTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LastCompletedSyncTransformer.m; path = "pithos-macos/LastCompletedSyncTransformer.m"; sourceTree = "<group>"; };
                616FC0AE13F91BA400140A33 /* PithosObjectNodeInfoController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PithosObjectNodeInfoController.xib; sourceTree = "<group>"; };
                616FC0B113F97D0800140A33 /* PithosNodeInfoController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PithosNodeInfoController.h; sourceTree = "<group>"; };
                616FC0B213F97D0800140A33 /* PithosNodeInfoController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PithosNodeInfoController.m; sourceTree = "<group>"; };
                                6152D091143334CC00803874 /* AllowedToBoolTransformer.m */,
                                613629B3143E0F8B00363787 /* GroupMembersDictionaryTransformer.h */,
                                613629B4143E0F8B00363787 /* GroupMembersDictionaryTransformer.m */,
+                               6164B2DB1450E55C00D4C1AB /* LastCompletedSyncTransformer.h */,
+                               6164B2DC1450E55C00D4C1AB /* LastCompletedSyncTransformer.m */,
                        );
                        name = "Value Transformers";
                        sourceTree = "<group>";
                                61F040F31448547000A0C788 /* FileMD5Hash.c in Sources */,
                                61F04132144DB97200A0C788 /* PithosLocalObjectState.m in Sources */,
                                61F04133144DB97600A0C788 /* PithosSyncDaemon.m in Sources */,
+                               6164B2DD1450E55C00D4C1AB /* LastCompletedSyncTransformer.m in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
diff --git a/pithos-macos/LastCompletedSyncTransformer.h b/pithos-macos/LastCompletedSyncTransformer.h
new file mode 100644 (file)
index 0000000..af6c41d
--- /dev/null
@@ -0,0 +1,39 @@
+//
+//  LastCompletedSyncTransformer.h
+//  pithos-macos
+//
+// Copyright 2011 GRNET S.A. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or
+// without modification, are permitted provided that the following
+// conditions are met:
+// 
+//   1. Redistributions of source code must retain the above
+//      copyright notice, this list of conditions and the following
+//      disclaimer.
+// 
+//   2. Redistributions in binary form must reproduce the above
+//      copyright notice, this list of conditions and the following
+//      disclaimer in the documentation and/or other materials
+//      provided with the distribution.
+// 
+// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+// 
+// The views and conclusions contained in the software and
+// documentation are those of the authors and should not be
+// interpreted as representing official policies, either expressed
+// or implied, of GRNET S.A.
+
+@interface LastCompletedSyncTransformer : NSValueTransformer
+@end
diff --git a/pithos-macos/LastCompletedSyncTransformer.m b/pithos-macos/LastCompletedSyncTransformer.m
new file mode 100644 (file)
index 0000000..ecdaf44
--- /dev/null
@@ -0,0 +1,81 @@
+//
+//  LastCompletedSyncTransformer.m
+//  pithos-macos
+//
+// Copyright 2011 GRNET S.A. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or
+// without modification, are permitted provided that the following
+// conditions are met:
+// 
+//   1. Redistributions of source code must retain the above
+//      copyright notice, this list of conditions and the following
+//      disclaimer.
+// 
+//   2. Redistributions in binary form must reproduce the above
+//      copyright notice, this list of conditions and the following
+//      disclaimer in the documentation and/or other materials
+//      provided with the distribution.
+// 
+// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+// 
+// The views and conclusions contained in the software and
+// documentation are those of the authors and should not be
+// interpreted as representing official policies, either expressed
+// or implied, of GRNET S.A.
+
+#import "LastCompletedSyncTransformer.h"
+
+@implementation LastCompletedSyncTransformer
+
++ (Class)transformedValueClass {
+       return [NSString class];
+}
+
++ (BOOL)allowsReverseTransformation {
+       return NO;
+}
+
+- (id)transformedValue:(id)value {
+    if (value == nil)
+               return @"Last Sync: -";
+    
+    NSCalendar *calendar = [NSCalendar currentCalendar];
+    NSDateComponents *comps = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:[NSDate date]];
+    [comps setHour:23];
+    [comps setMinute:59];
+    [comps setSecond:59];    
+    NSTimeInterval timeInterval = [[calendar dateFromComponents:comps] timeIntervalSinceDate:(NSDate *)value];
+    if (timeInterval < 86400) {
+        // Today
+        return [NSString stringWithFormat:@"Last Sync: Today %@", [NSDateFormatter localizedStringFromDate:(NSDate *)value 
+                                                                                                     dateStyle:NSDateFormatterNoStyle 
+                                                                                                     timeStyle:NSDateFormatterShortStyle]];
+    } else if (timeInterval < 172800) {
+        // Yesterday
+        return [NSString stringWithFormat:@"Last Sync: Yesterday %@", [NSDateFormatter localizedStringFromDate:(NSDate *)value 
+                                                                                           dateStyle:NSDateFormatterNoStyle 
+                                                                                           timeStyle:NSDateFormatterShortStyle]];
+    } else {
+        return [NSString stringWithFormat:@"Last Sync: %@", [NSDateFormatter localizedStringFromDate:(NSDate *)value 
+                                                                                           dateStyle:NSDateFormatterShortStyle 
+                                                                                           timeStyle:NSDateFormatterShortStyle]];
+    }
+}
+
++ (void)initialize {
+    [[NSValueTransformer class] setValueTransformer:[self new] forName:@"LastCompletedSyncTransformer"];
+}
+
+@end
index a5420b4..b14fda6 100644 (file)
 - (id)transformedValue:(id)value {
     if (value == nil)
                return nil;
-    // XXX make it more like finder
-    return [NSDateFormatter localizedStringFromDate:(NSDate *)value 
-                                          dateStyle:NSDateFormatterShortStyle 
-                                          timeStyle:NSDateFormatterShortStyle];
+    
+    NSCalendar *calendar = [NSCalendar currentCalendar];
+    NSDateComponents *comps = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:[NSDate date]];
+    [comps setHour:23];
+    [comps setMinute:59];
+    [comps setSecond:59];    
+    NSTimeInterval timeInterval = [[calendar dateFromComponents:comps] timeIntervalSinceDate:(NSDate *)value];
+    if (timeInterval < 86400) {
+        // Today
+        return [NSString stringWithFormat:@"Today %@", [NSDateFormatter localizedStringFromDate:(NSDate *)value 
+                                                                                                 dateStyle:NSDateFormatterNoStyle 
+                                                                                                 timeStyle:NSDateFormatterShortStyle]];
+    } else if (timeInterval < 172800) {
+        // Yesterday
+        return [NSString stringWithFormat:@"Yesterday %@", [NSDateFormatter localizedStringFromDate:(NSDate *)value 
+                                                                                                     dateStyle:NSDateFormatterNoStyle 
+                                                                                                     timeStyle:NSDateFormatterShortStyle]];
+    } else {
+        return [NSDateFormatter localizedStringFromDate:(NSDate *)value 
+                                              dateStyle:NSDateFormatterShortStyle 
+                                              timeStyle:NSDateFormatterShortStyle];
+    }
 }
 
 + (void)initialize {
index 8b454a3..6c7910f 100644 (file)
@@ -56,6 +56,8 @@
     
     NSUInteger syncOperationCount;
     BOOL newSyncRequested;
+    NSDate *lastCompletedSync;
+    BOOL syncIncomplete;
     
     ASINetworkQueue *queue;
     NSTimer *timer;
@@ -72,6 +74,8 @@
 @property (nonatomic, readonly) NSString *pithosStateFilePath;
 @property (nonatomic, readonly) NSString *tempDownloadsDirPath;
 
+@property (nonatomic, retain) NSDate *lastCompletedSync;
+
 - (id)initWithDirectoryPath:(NSString *)aDirectoryPath 
               containerName:(NSString *)aContainerName 
                timeInterval:(NSTimeInterval)aTimeInterval;
index a06372f..8631443 100644 (file)
                                 object:(ASIPithosObject *)object 
                          localFilePath:(NSString *)filePath;
 - (void)requestFailed:(ASIPithosRequest *)request;
+
+- (void)increaseSyncOperationCount;
+- (void)decreaseSyncOperationCount;
+
 @end
 
 @implementation PithosSyncDaemon
-@synthesize blockHash, blockSize, lastModified, remoteObjects, storedLocalObjectStates;
+@synthesize blockHash, blockSize, lastModified, lastCompletedSync, remoteObjects, storedLocalObjectStates;
 @synthesize pithosStateFilePath, tempDownloadsDirPath;
 
 #pragma mark -
     [storedLocalObjectStates release];
     [remoteObjects release];
     [objects release];
+    [lastCompletedSync release];
     [lastModified release];
     [blockHash release];
     [containerName release];
     [data writeToFile:self.pithosStateFilePath atomically:YES];
 }
 
+- (void)increaseSyncOperationCount {
+    @synchronized(self) {
+        syncOperationCount++;
+    }
+}
+
+- (void)decreaseSyncOperationCount {
+    @synchronized(self) {
+        syncOperationCount--;
+        if (!syncOperationCount && !syncIncomplete) {
+            self.lastCompletedSync = [NSDate date];
+            [activityFacility startAndEndActivityWithType:PithosActivityOther 
+                                                  message:[NSString stringWithFormat:@"Sync: Completed %@", lastCompletedSync]];
+        }
+    }
+}
+
 - (void)sync {
     @synchronized(self) {
         if (syncOperationCount) {
             // The first operation is the server listing
             syncOperationCount = 1;
             newSyncRequested = NO;
+            syncIncomplete = NO;
         }
     }
 
                                                                message:@"Sync: Getting server listing"];
     containerRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                  activity, @"activity", 
+                                 @"Sync: Getting server listing (stopped)", @"stoppedActivityMessage", 
+                                 @"Sync: Getting server listing (failed)", @"failedActivityMessage", 
+                                 @"Sync: Getting server listing (finished)", @"finishedActivityMessage", 
+                                 [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", 
                                  [NSNumber numberWithUnsignedInteger:10], @"retries", 
                                  nil];
-//    [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
     [queue addOperation:[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh]];
 }
 
                                                   message:[NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name]];
     } else if (storedState.tmpDownloadFile == nil) {
         // Create new local object
-        @synchronized(self) {
-            syncOperationCount++;
-        }
+        [self increaseSyncOperationCount];
         __block ASIPithosObjectRequest *objectRequest = [PithosUtilities objectBlockDataRequestWithContainerName:containerName 
                                                                                                           object:object 
                                                                                                       blockIndex:0 
                                   [NSString stringWithFormat:@"Sync: Downloading '%@' (stopped)", object.name], @"stoppedActivityMessage", 
                                   [NSString stringWithFormat:@"Sync: Downloading '%@' (failed)", object.name], @"failedActivityMessage", 
                                   [NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name], @"finishedActivityMessage", 
-                                  [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
+                                  [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
                                   [NSNumber numberWithUnsignedInteger:10], @"retries", 
                                   nil];
         [objectRequest setBytesReceivedBlock:^(unsigned long long size, unsigned long long total){
                                   totalBytes:activity.totalBytes 
                                 currentBytes:(activity.currentBytes + size)];
         }];
-//        [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
-        [queue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
+        [queue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]];
     } else {
         // Resume local object download
-        @synchronized(self) {
-            syncOperationCount++;
-        }
+        [self increaseSyncOperationCount];
         ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest objectHashmapRequestWithContainerName:containerName 
                                                                                                    objectName:object.name];
         objectRequest.delegate = self;
                                   [NSString stringWithFormat:@"Sync: Downloading '%@' (stopped)", object.name], @"stoppedActivityMessage", 
                                   [NSString stringWithFormat:@"Sync: Downloading '%@' (failed)", object.name], @"failedActivityMessage", 
                                   [NSString stringWithFormat:@"Sync: Downloading '%@' (100%%)", object.name], @"finishedActivityMessage", 
-                                  [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
+                                  [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
                                   [NSNumber numberWithUnsignedInteger:10], @"retries", 
                                   nil];
-//        [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
-        [queue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
+        [queue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]];
     }
 }
 
 -(void)updateServerStateWithCurrentState:(PithosLocalObjectState *)currentState 
                                   object:(ASIPithosObject *)object 
                            localFilePath:(NSString *)filePath {
+    [self increaseSyncOperationCount];
     if (currentState.isDirectory) {
         // Create remote directory object
-        @synchronized(self) {
-            syncOperationCount++;
-        }
         ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithContainerName:containerName 
                                                                                                      objectName:object.name 
                                                                                                            eTag:nil 
                                   [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
                                   [NSNumber numberWithUnsignedInteger:10], @"retries", 
                                   nil];
-//        [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
         [queue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
     } else if ([currentState.md5 isEqualToString:@" "] && [currentState.hashMapHash isEqualToString:@" "]) {
         // Delete remote object
-        @synchronized(self) {
-            syncOperationCount++;
+        NSString *safeObjectName = [PithosUtilities safeObjectNameForContainerName:@"trash" 
+                                                                        objectName:object.name];
+        if (safeObjectName) {
+            ASIPithosObjectRequest *objectRequest = [PithosUtilities moveObjectRequestWithContainerName:containerName 
+                                                                                             objectName:object.name 
+                                                                               destinationContainerName:@"trash" 
+                                                                                  destinationObjectName:safeObjectName 
+                                                                                          checkIfExists:NO];
+            if (objectRequest) {
+                objectRequest.delegate = self;
+                objectRequest.didFinishSelector = @selector(moveObjectToTrashFinished:);
+                objectRequest.didFailSelector = @selector(requestFailed:);
+                PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete 
+                                                                           message:[NSString stringWithFormat:@"Sync: Moving to trash '%@'", object.name]];
+                objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                          object, @"pithosObject", 
+                                          activity, @"activity", 
+                                          [NSString stringWithFormat:@"Sync: Moving to trash '%@' (stopped)", object.name], @"stoppedActivityMessage", 
+                                          [NSString stringWithFormat:@"Sync: Moving to trash '%@' (failed)", object.name], @"failedActivityMessage", 
+                                          [NSString stringWithFormat:@"Sync: Moving to trash '%@' (finished)", object.name], @"finishedActivityMessage", 
+                                          [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
+                                          [NSNumber numberWithUnsignedInteger:10], @"retries", 
+                                          nil];
+                [queue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
+            } else {
+                syncIncomplete = YES;
+                [self decreaseSyncOperationCount];
+            }
+        } else {
+            syncIncomplete = YES;
+            [self decreaseSyncOperationCount];
         }
-        ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest deleteObjectRequestWithContainerName:containerName 
-                                                                                                  objectName:object.name];
-        objectRequest.delegate = self;
-        objectRequest.didFinishSelector = @selector(deleteObjectFinished:);
-        objectRequest.didFailSelector = @selector(requestFailed:);
-        PithosActivity *activity = [activityFacility startActivityWithType:PithosActivityDelete 
-                                                                   message:[NSString stringWithFormat:@"Sync: Deleting '%@'", object.name]];
-        objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
-                                  object, @"pithosObject", 
-                                  activity, @"activity", 
-                                  [NSString stringWithFormat:@"Sync: Deleting '%@' (stopped)", object.name], @"stoppedActivityMessage", 
-                                  [NSString stringWithFormat:@"Sync: Deleting '%@' (failed)", object.name], @"failedActivityMessage", 
-                                  [NSString stringWithFormat:@"Sync: Deleting '%@' (finished)", object.name], @"finishedActivityMessage", 
-                                  [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
-                                  [NSNumber numberWithUnsignedInteger:10], @"retries", 
-                                  nil];
-//        [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
-        [queue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
     } else {
         // Upload file to remote object
         NSError *error = nil;
                                                                                                   hashes:&hashes 
                                                                                           sharingAccount:nil];
         if (objectRequest) {
-            @synchronized(self) {
-                syncOperationCount++;
-            }
             objectRequest.delegate = self;
             objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
             objectRequest.didFailSelector = @selector(requestFailed:);
               [NSString stringWithFormat:@"Sync: Uploading '%@' (stopped)", object.name], @"stoppedActivityMessage", 
               [NSString stringWithFormat:@"Sync: Uploading '%@' (failed)", object.name], @"failedActivityMessage", 
               [NSString stringWithFormat:@"Sync: Uploading '%@' (100%%)", object.name], @"finishedActivityMessage", 
-              [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
+              [NSNumber numberWithInteger:NSOperationQueuePriorityNormal], @"priority", 
               [NSNumber numberWithUnsignedInteger:10], @"retries", 
               nil]];
-//            [[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
-            [queue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityHigh]];
+            [queue addOperation:[PithosUtilities prepareRequest:objectRequest priority:NSOperationQueuePriorityNormal]];
+        } else {
+            syncIncomplete = YES;
+            [self decreaseSyncOperationCount];
         }
     }
 
                 newContainerRequest.didFailSelector = @selector(listRequestFailed:);
                 newContainerRequest.userInfo = newContainerRequest.userInfo;
                 [(NSMutableDictionary *)newContainerRequest.userInfo setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
-//                [[PithosUtilities prepareRequest:newContainerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
-                [queue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:NSOperationQueuePriorityHigh]];
+                [queue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
                 return;
             }
         }
         [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:@"Sync: Getting server listing (finished)"];
+                          withMessage:[containerRequest.userInfo objectForKey:@"finishedActivityMessage"]];
         NSFileManager *fileManager = [NSFileManager defaultManager];
         NSString *containerDirectoryPath = [directoryPath stringByAppendingPathComponent:containerName];
         NSError *error = nil;
             }
         }
         @synchronized(self) {
-            syncOperationCount--;
+            [self decreaseSyncOperationCount];
             if (newSyncRequested && !syncOperationCount)
                 [self sync];
         }
         if (retries > 0) {
             ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:containerRequest];
             [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-//            [[PithosUtilities prepareRequest:newContainerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
-            [queue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:NSOperationQueuePriorityHigh]];
+            [queue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
         } else {
             [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
-                              withMessage:@"Sync: Getting server listing (failed)"];
+                              withMessage:[containerRequest.userInfo objectForKey:@"failedActivityMessage"]];
             @synchronized(self) {
                 // Since the server listing failed in all retries, the operation finished and the sync cycle is completeted unsuccesfully
                 syncOperationCount = 0;
 - (void)listRequestFailed:(ASIPithosContainerRequest *)containerRequest {
     if ([containerRequest isCancelled]) {
         [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:@"Sync: Getting server listing (stopped)"];
+                          withMessage:[containerRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
         [objects release];
         objects = nil;
         @synchronized(self) {
     if (retries > 0) {
         ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:containerRequest];
         [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-//        [[PithosUtilities prepareRequest:newContainerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
-        [queue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:NSOperationQueuePriorityVeryHigh]];
+        [queue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
     } else {
         [activityFacility endActivity:[containerRequest.userInfo objectForKey:@"activity"] 
-                          withMessage:@"Sync: Getting server listing (failed)"];
+                          withMessage:[containerRequest.userInfo objectForKey:@"failedActivityMessage"]];
         [objects release];
         objects = nil;
         @synchronized(self) {
             [activityFacility endActivity:activity 
                               withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
             @synchronized(self) {
-                syncOperationCount--;
+                syncIncomplete = YES;
+                [self decreaseSyncOperationCount];
                 if (newSyncRequested && !syncOperationCount)
                     [self sync];
             }
                 [activityFacility endActivity:activity 
                                   withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
                 @synchronized(self) {
-                    syncOperationCount--;
+                    syncIncomplete = YES;
+                    [self decreaseSyncOperationCount];
                     if (newSyncRequested && !syncOperationCount)
                         [self sync];
                 }
                     [activityFacility endActivity:activity 
                                       withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
                     @synchronized(self) {
-                        syncOperationCount--;
+                        syncIncomplete = YES;
+                        [self decreaseSyncOperationCount];
                         if (newSyncRequested && !syncOperationCount)
                             [self sync];
                     }
                     [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
                                       withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
                     @synchronized(self) {
-                        syncOperationCount--;
+                        syncIncomplete = YES;
+                        [self decreaseSyncOperationCount];
                         if (newSyncRequested && !syncOperationCount)
                             [self sync];
                     }
                 [activityFacility endActivity:activity 
                                   withMessage:[objectRequest.userInfo objectForKey:@"failedActivityMessage"]];
                 @synchronized(self) {
-                    syncOperationCount--;
+                    syncIncomplete = YES;
+                    [self decreaseSyncOperationCount];
                     if (newSyncRequested && !syncOperationCount)
                         [self sync];
                 }
             [self saveLocalState];
             
             @synchronized(self) {
-                syncOperationCount--;
+                [self decreaseSyncOperationCount];
                 if (newSyncRequested && !syncOperationCount)
                     [self sync];
             }
                 [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
                                   withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
                 @synchronized(self) {
-                    syncOperationCount--;
+                    syncIncomplete = YES;
+                    [self decreaseSyncOperationCount];
                     if (!syncOperationCount)
                         [self sync];
                 }
                                           totalBytes:activity.totalBytes 
                                         currentBytes:(activity.currentBytes + size)];
                 }];
-//                [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
-                [queue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityHigh]];
+                [queue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]];
             }
         }
     } else if (objectRequest.responseStatusCode == 412) {
         [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
                           withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
         @synchronized(self) {
-            syncOperationCount--;
+            syncIncomplete = YES;
+            [self decreaseSyncOperationCount];
             if (newSyncRequested && !syncOperationCount)
                 [self sync];
         }
             [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
                               withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
             @synchronized(self) {
-                syncOperationCount--;
+                syncIncomplete = YES;
+                [self decreaseSyncOperationCount];
                 if (!syncOperationCount)
                     [self sync];
             }
                                       totalBytes:activity.totalBytes 
                                     currentBytes:(activity.currentBytes + size)];
             }];
-//            [[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
-            [queue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:NSOperationQueuePriorityHigh]];
+            [queue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]];
         }
     } else {
         [self requestFailed:objectRequest];
         [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
                           withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
         @synchronized(self) {
-            syncOperationCount--;
+            [self decreaseSyncOperationCount];
             if (newSyncRequested && !syncOperationCount)
                 [self sync];
         }
     }
 }
 
-- (void)deleteObjectFinished:(ASIPithosObjectRequest *)objectRequest {
-    NSLog(@"Sync::delete object finished: %@", objectRequest.url);
-    if (objectRequest.responseStatusCode == 204) {
+- (void)moveObjectToTrashFinished:(ASIPithosObjectRequest *)objectRequest {
+    NSLog(@"Sync::move object to trash finished: %@", objectRequest.url);
+    if (objectRequest.responseStatusCode == 201) {
         [storedLocalObjectStates removeObjectForKey:[[objectRequest.userInfo objectForKey:@"pithosObject"] name]];
         [self saveLocalState];
         [activityFacility endActivity:[objectRequest.userInfo objectForKey:@"activity"] 
                           withMessage:[objectRequest.userInfo objectForKey:@"finishedActivityMessage"]];
         @synchronized(self) {
-            syncOperationCount--;
+            [self decreaseSyncOperationCount];
             if (newSyncRequested && !syncOperationCount)
                 [self sync];
         }
                            totalBytes:totalBytes 
                          currentBytes:totalBytes];
         @synchronized(self) {
-            syncOperationCount--;
+            [self decreaseSyncOperationCount];
             if (newSyncRequested && !syncOperationCount)
                 [self sync];
         }
             [activityFacility endActivity:activity 
                               withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
             @synchronized(self) {
-                syncOperationCount--;
+                syncIncomplete = YES;
+                [self decreaseSyncOperationCount];
                 if (!syncOperationCount)
                     [self sync];
             }
             if (iteration == 0) {
                 NSLog(@"Sync::upload iteration limit reached: %@", objectRequest.url);
                 [activityFacility endActivity:activity 
-                                  withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]]; 
-                @synchronized(self) {
-                    syncOperationCount--;
-                }
+                                  withMessage:[objectRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
+                syncIncomplete = YES;
+                [self decreaseSyncOperationCount];
                 return;
             }
             NSLog(@"Sync::object is missing hashes: %@", objectRequest.url);
                                       totalBytes:activity.totalBytes 
                                     currentBytes:(activity.currentBytes + size)];
             }];
-//            [[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
             [queue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
         }
     } else {
             [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:10] forKey:@"retries"];
             [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlocks"];
             [(NSMutableDictionary *)(newObjectRequest.userInfo) removeObjectForKey:@"missingBlockIndex"];
-//            [[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
             [queue addOperation:[PithosUtilities prepareRequest:newObjectRequest priority:[[newObjectRequest.userInfo objectForKey:@"priority"] integerValue]]];
         } else {
             if (newSyncRequested) {
                 [activityFacility endActivity:activity 
                                   withMessage:[containerRequest.userInfo objectForKey:@"stoppedActivityMessage"]];
                 @synchronized(self) {
-                    syncOperationCount--;
+                    syncIncomplete = YES;
+                    [self decreaseSyncOperationCount];
                     if (!syncOperationCount)
                         [self sync];
                 }
                                           totalBytes:activity.totalBytes 
                                         currentBytes:(activity.currentBytes + size)];
                 }];
-//                [[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
                 [queue addOperation:[PithosUtilities prepareRequest:newContainerRequest priority:[[newContainerRequest.userInfo objectForKey:@"priority"] integerValue]]];
             }
         }
     if ([request isCancelled]) {
         [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
                           withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
-        @synchronized(self) {
-            syncOperationCount--;
-        }
+        syncIncomplete = YES;
+        [self decreaseSyncOperationCount];
         return;
     }
     if (newSyncRequested) {
         [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
                           withMessage:[request.userInfo objectForKey:@"stoppedActivityMessage"]];
         @synchronized(self) {
-            syncOperationCount--;
+            syncIncomplete = YES;
+            [self decreaseSyncOperationCount];
             if (!syncOperationCount)
                 [self sync];
         }
     if (retries > 0) {
         ASIPithosRequest *newRequest = (ASIPithosRequest *)[PithosUtilities copyRequest:request];
         [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
-//        [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
         [queue addOperation:[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]]];
     } else {
         [activityFacility endActivity:[request.userInfo objectForKey:@"activity"] 
                           withMessage:[request.userInfo objectForKey:@"failedActivityMessage"]];
-        @synchronized(self) {
-            syncOperationCount--;
-        }
+        syncIncomplete = YES;
+        [self decreaseSyncOperationCount];
     }
 }
 
index fdd2fc2..700229d 100644 (file)
                                        </object>
                                        <int key="connectionID">561</int>
                                </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBBindingConnection" key="connection">
+                                               <string key="label">title: delegate.pithosSyncDaemon.lastCompletedSync</string>
+                                               <reference key="source" ref="62426625"/>
+                                               <reference key="destination" ref="1050"/>
+                                               <object class="NSNibBindingConnector" key="connector">
+                                                       <reference key="NSSource" ref="62426625"/>
+                                                       <reference key="NSDestination" ref="1050"/>
+                                                       <string key="NSLabel">title: delegate.pithosSyncDaemon.lastCompletedSync</string>
+                                                       <string key="NSBinding">title</string>
+                                                       <string key="NSKeyPath">delegate.pithosSyncDaemon.lastCompletedSync</string>
+                                                       <object class="NSDictionary" key="NSOptions">
+                                                               <string key="NS.key.0">NSValueTransformerName</string>
+                                                               <string key="NS.object.0">LastCompletedSyncTransformer</string>
+                                                       </object>
+                                                       <int key="NSNibBindingConnectorVersion">2</int>
+                                               </object>
+                                       </object>
+                                       <int key="connectionID">565</int>
+                               </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBBindingConnection" key="connection">
+                                               <string key="label">enabled: delegate.alwaysNo</string>
+                                               <reference key="source" ref="62426625"/>
+                                               <reference key="destination" ref="1050"/>
+                                               <object class="NSNibBindingConnector" key="connector">
+                                                       <reference key="NSSource" ref="62426625"/>
+                                                       <reference key="NSDestination" ref="1050"/>
+                                                       <string key="NSLabel">enabled: delegate.alwaysNo</string>
+                                                       <string key="NSBinding">enabled</string>
+                                                       <string key="NSKeyPath">delegate.alwaysNo</string>
+                                                       <int key="NSNibBindingConnectorVersion">2</int>
+                                               </object>
+                                       </object>
+                                       <int key="connectionID">569</int>
+                               </object>
                        </object>
                        <object class="IBMutableOrderedSet" key="objectRecords">
                                <object class="NSArray" key="orderedObjects">
                                <reference key="dict.values" ref="0"/>
                        </object>
                        <nil key="sourceID"/>
-                       <int key="maxID">561</int>
+                       <int key="maxID">569</int>
                </object>
                <object class="IBClassDescriber" key="IBDocument.Classes"/>
                <int key="IBDocument.localizationMode">0</int>
index c684274..93f954a 100644 (file)
@@ -44,7 +44,8 @@
     IBOutlet PithosBrowserController *pithosBrowserController;
     IBOutlet PithosPreferencesController *pithosPreferencesController;
     
-    PithosSyncDaemon *pithosSyncDaemon;
+    IBOutlet PithosSyncDaemon *pithosSyncDaemon;
+    BOOL alwaysNo;
 
     IBOutlet NSMenu *statusMenu;
     NSStatusItem *statusItem;
@@ -78,4 +79,7 @@
 @property (nonatomic, readonly) NSString *syncContainerName;
 @property (nonatomic, assign) NSTimeInterval syncTimeInterval;
 
+@property (nonatomic, retain) PithosSyncDaemon *pithosSyncDaemon;
+@property (nonatomic, assign) BOOL alwaysNo;
+
 @end
index 235a9f7..b05ac13 100644 (file)
@@ -44,7 +44,7 @@
 
 @implementation pithos_macosAppDelegate
 @synthesize storageURLPrefix, publicURLPrefix, loginURLPrefix, aboutURL;
-@synthesize syncDirectoryPath, syncContainerName, syncTimeInterval;
+@synthesize syncDirectoryPath, syncContainerName, syncTimeInterval, pithosSyncDaemon, alwaysNo;
 
 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
     NSURL *testURL;
@@ -99,7 +99,7 @@
     [syncDirectoryPath retain];
     
     syncContainerName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"PithosSyncContainerName"];
-    if (!syncContainerName || ![syncContainerName length]) {
+    if (!syncContainerName || ![syncContainerName length] || [syncContainerName isEqualToString:@"trash"]) {
         syncContainerName = [NSString stringWithString:@"pithos"];
     }
     [syncContainerName retain];
     [statusItem setMenu:statusMenu];
     [statusItem setImage:sourceImage];
     [statusItem setHighlightMode:YES];
+    
+    self.alwaysNo = NO;
 }
 
 - (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent {
 }
 
 - (void)startSyncWithDirectoryPath:(NSString *)directoryPath containerName:(NSString *)containerName {
-    [pithosSyncDaemon release];
-    pithosSyncDaemon = [[PithosSyncDaemon alloc] initWithDirectoryPath:syncDirectoryPath 
+    self.pithosSyncDaemon = [[PithosSyncDaemon alloc] initWithDirectoryPath:syncDirectoryPath 
                                                          containerName:syncContainerName 
                                                           timeInterval:syncTimeInterval];
 }