[ios] Webstorage backup to cloud/local-only

LocalStorage and WebSQL databases have been manually backed up in
cordova since iOS5.1 moved them into a Caches directory.  iOS6 adds a
UserDefaults setting to automatically persist these databases, but with
the added feature of backup up to iCloud.

We wanted to allow disabling iCloud backup on iOS6, as well as marking
our manual backups to not be backed up to cloud on iOS5.1.  This patch
changes the BackupWebStorage plist option from a boolean to a 3-option
string: [none, local, cloud].  Toggling between none/local/cloud as well
as upgrading ios versions should work correctly.


Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/commit/a39921be
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/tree/a39921be
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/diff/a39921be

Branch: refs/heads/master
Commit: a39921bed239706dc4a0b3a22762d97bc0b2c993
Parents: 78ba908
Author: Michal Mocny <mmo...@gmail.com>
Authored: Mon Oct 22 13:44:03 2012 -0400
Committer: Michal Mocny <mmo...@gmail.com>
Committed: Mon Oct 22 13:44:03 2012 -0400

----------------------------------------------------------------------
 CordovaLib/Classes/CDVLocalStorage.h            |    3 +-
 CordovaLib/Classes/CDVLocalStorage.m            |   89 +++++++++++-------
 CordovaLib/Classes/CDVViewController.m          |   25 +++--
 bin/templates/project/__TESTING__/Cordova.plist |    2 +-
 4 files changed, 71 insertions(+), 48 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/a39921be/CordovaLib/Classes/CDVLocalStorage.h
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVLocalStorage.h 
b/CordovaLib/Classes/CDVLocalStorage.h
index 251eb17..e5e3112 100644
--- a/CordovaLib/Classes/CDVLocalStorage.h
+++ b/CordovaLib/Classes/CDVLocalStorage.h
@@ -31,12 +31,11 @@
 - (void)backup:(CDVInvokedUrlCommand*)command;
 - (void)restore:(CDVInvokedUrlCommand*)command;
 
-+ (void)__verifyAndFixDatabaseLocations;
++ (void)__fixupDatabaseLocationsWithBackupType:(NSString*)backupType;
 // Visible for testing.
 + 
(BOOL)__verifyAndFixDatabaseLocationsWithAppPlistDict:(NSMutableDictionary*)appPlistDict
     bundlePath                                          :(NSString*)bundlePath
    fileManager                                         
:(NSFileManager*)fileManager;
-+ (void)__fixLegacyDatabaseLocationIssues;
 @end
 
 @interface CDVBackupInfo : NSObject

http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/a39921be/CordovaLib/Classes/CDVLocalStorage.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVLocalStorage.m 
b/CordovaLib/Classes/CDVLocalStorage.m
index 7d39052..79e8db7 100644
--- a/CordovaLib/Classes/CDVLocalStorage.m
+++ b/CordovaLib/Classes/CDVLocalStorage.m
@@ -31,21 +31,18 @@
 
 @synthesize backupInfo, webviewDelegate;
 
-- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView
+- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView 
settings:(NSDictionary*)classSettings
 {
-    self = (CDVLocalStorage*)[super initWithWebView:theWebView];
+    self = (CDVLocalStorage*)[super initWithWebView:theWebView 
settings:classSettings];
     if (self) {
         [[NSNotificationCenter defaultCenter] addObserver:self 
selector:@selector(onResignActive)
                                                      
name:UIApplicationWillResignActiveNotification object:nil];
 
-        self.backupInfo = [[self class] createBackupInfo];
+        self.backupInfo = [[self class] 
createBackupInfoWithCloudBackup:((NSString*)[classSettings 
objectForKey:@"backupType"] == @"cloud")];
 
         // over-ride current webview delegate (for restore reasons)
         self.webviewDelegate = theWebView.delegate;
         theWebView.delegate = self;
-
-        // verify the and fix the iOS 5.1 database locations once
-        [[self class] __verifyAndFixDatabaseLocations];
     }
 
     return self;
@@ -54,7 +51,7 @@
 #pragma mark -
 #pragma mark Plugin interface methods
 
-+ (NSMutableArray*)createBackupInfoWithTargetDir:(NSString*)targetDir 
backupDir:(NSString*)backupDir rename:(BOOL)rename
++ (NSMutableArray*)createBackupInfoWithTargetDir:(NSString*)targetDir 
backupDir:(NSString*)backupDir targetDirNests:(BOOL)targetDirNests 
backupDirNests:(BOOL)backupDirNests rename:(BOOL)rename
 {
     NSMutableArray* backupInfo = [NSMutableArray arrayWithCapacity:3];
 
@@ -64,8 +61,9 @@
 
     // ////////// LOCALSTORAGE
 
-    original = [targetDir 
stringByAppendingPathComponent:@"file__0.localstorage"];
-    backup = [backupDir stringByAppendingPathComponent:rename ? 
@"localstorage.appdata.db":@"file__0.localstorage"];
+    original = [targetDir stringByAppendingPathComponent:targetDirNests ? 
@"WebKit/LocalStorage/file__0.localstorage":@"file__0.localstorage"];
+    backup = [backupDir stringByAppendingPathComponent:(backupDirNests ? 
@"WebKit/LocalStorage":@"")];
+    backup = [backupDir stringByAppendingPathComponent:(rename ? 
@"localstorage.appdata.db":@"file__0.localstorage")];
 
     backupItem = [[CDVBackupInfo alloc] init];
     backupItem.backup = backup;
@@ -76,8 +74,9 @@
 
     // ////////// WEBSQL MAIN DB
 
-    original = [targetDir stringByAppendingPathComponent:@"Databases.db"];
-    backup = [backupDir stringByAppendingPathComponent:rename ? 
@"websqlmain.appdata.db":@"Databases.db"];
+    original = [targetDir stringByAppendingPathComponent:targetDirNests ? 
@"WebKit/Databases/Databases.db":@"Databases.db"];
+    backup = [backupDir stringByAppendingPathComponent:(backupDirNests ? 
@"WebKit/Databases":@"")];
+    backup = [backupDir stringByAppendingPathComponent:(rename ? 
@"websqlmain.appdata.db":@"Databases.db")];
 
     backupItem = [[CDVBackupInfo alloc] init];
     backupItem.backup = backup;
@@ -88,8 +87,9 @@
 
     // ////////// WEBSQL DATABASES
 
-    original = [targetDir stringByAppendingPathComponent:@"file__0"];
-    backup = [backupDir stringByAppendingPathComponent:rename ? 
@"websqldbs.appdata.db":@"file__0"];
+    original = [targetDir stringByAppendingPathComponent:targetDirNests ? 
@"WebKit/Databases/file__0":@"file__0"];
+    backup = [backupDir stringByAppendingPathComponent:(backupDirNests ? 
@"WebKit/Databases":@"")];
+    backup = [backupDir stringByAppendingPathComponent:(rename ? 
@"websqldbs.appdata.db":@"file__0")];
 
     backupItem = [[CDVBackupInfo alloc] init];
     backupItem.backup = backup;
@@ -101,16 +101,32 @@
     return backupInfo;
 }
 
-+ (NSMutableArray*)createBackupInfo
++ (NSMutableArray*)createBackupInfoWithCloudBackup:(BOOL)cloudBackup
 {
+    // create backup info from backup folder to caches folder
     NSString* appLibraryFolder = [NSSearchPathForDirectoriesInDomains 
(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
+    NSString* appDocumentsFolder = [NSSearchPathForDirectoriesInDomains 
(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
     NSString* cacheFolder = [appLibraryFolder 
stringByAppendingPathComponent:@"Caches"];
-    NSString* backupsFolder = [appLibraryFolder 
stringByAppendingPathComponent:@"Backups"];
+    NSString* backupsFolder = [appDocumentsFolder 
stringByAppendingPathComponent:@"Backups"];
 
-    // create the backups folder
+    // create the backups folder, if needed
     [[NSFileManager defaultManager] createDirectoryAtPath:backupsFolder 
withIntermediateDirectories:YES attributes:nil error:nil];
+    [self addSkipBackupAttributeToItemAtURL:[NSURL 
fileURLWithPath:backupsFolder] skip:cloudBackup];
+
+    return [self createBackupInfoWithTargetDir:cacheFolder 
backupDir:backupsFolder targetDirNests:NO backupDirNests:NO rename:YES];
+}
 
-    return [self createBackupInfoWithTargetDir:cacheFolder 
backupDir:backupsFolder rename:YES];
++ (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL*)URL skip:(BOOL)skip
+{
+    assert(IsAtLeastiOSVersion(@"5.1"));
+    assert([[NSFileManager defaultManager] fileExistsAtPath:[URL path]]);
+
+    NSError* error = nil;
+    BOOL success = [URL setResourceValue:[NSNumber numberWithBool:skip] 
forKey:NSURLIsExcludedFromBackupKey error:&error];
+    if (!success) {
+        NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], 
error);
+    }
+    return success;
 }
 
 + (BOOL)copyFrom:(NSString*)src to:(NSString*)dest error:(NSError * 
__autoreleasing*)error
@@ -252,6 +268,12 @@
     }
 }
 
++ (void)__fixupDatabaseLocationsWithBackupType:(NSString*)backupType
+{
+    [self __verifyAndFixDatabaseLocations];
+    [self __restoreLegacyDatabaseLocationsWithBackupType:backupType];
+}
+
 + (void)__verifyAndFixDatabaseLocations
 {
     NSBundle* mainBundle = [NSBundle mainBundle];
@@ -303,31 +325,26 @@
     return dirty;
 }
 
-+ (void)__fixLegacyDatabaseLocationIssues
++ (void)__restoreLegacyDatabaseLocationsWithBackupType:(NSString*)backupType
 {
-    if (IsAtLeastiOSVersion(@"6.0")) {
-        // Ensure that the webview does not backup the databases to iCloud (We 
set to this true in 2.1, so need to reset it for upgraders).
-        [[NSUserDefaults standardUserDefaults] setBool:NO 
forKey:@"WebKitStoreWebDataForBackup"];
+    // on iOS 6, if you toggle between cloud/local backup, you must move 
database locations.  Default upgrade from iOS5.1 to iOS6 is like a toggle from 
local to cloud.
+    if (!IsAtLeastiOSVersion(@"6.0")) {
+        return;
     }
 
-    /*
-     * There are currently two legacy storage locations:
-     *   {Lib}/WebKit/LocalStorage which was used for store database (without 
backup renaming) in ios <5.1 and briefly for ios6, and
-     *   {Doc}/Backups which was used to store database backups (with backup 
renaming) with ios5.1+ until Apple started rejecting apps
-     *      for using the {Doc} folder to save non user generated content.
-     */
     NSString* appLibraryFolder = [NSSearchPathForDirectoriesInDomains 
(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
     NSString* appDocumentsFolder = [NSSearchPathForDirectoriesInDomains 
(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
 
-    // targetDir is where we want our databases to end up
-    NSString* targetDir = [appLibraryFolder 
stringByAppendingPathComponent:@"Caches"];
-
-    // backupDir's are the places where we may find old legacy backups
-    NSString* backupDir = [appDocumentsFolder 
stringByAppendingPathComponent:@"Backups"];
-    NSString* backupDir2 = [appLibraryFolder 
stringByAppendingPathComponent:@"WebKit/LocalStorage"];
+    NSMutableArray* backupInfo = nil;
 
-    NSMutableArray* backupInfo = [self createBackupInfoWithTargetDir:targetDir 
backupDir:backupDir rename:YES];
-    [backupInfo addObjectsFromArray:[self 
createBackupInfoWithTargetDir:targetDir backupDir:backupDir2 rename:NO]];
+    if ([backupType isEqualToString:@"cloud"]) {
+        // We would like to restore old backups/caches databases to the new 
destination (nested in lib folder)
+        [backupInfo addObjectsFromArray:[self 
createBackupInfoWithTargetDir:appLibraryFolder backupDir:[appDocumentsFolder 
stringByAppendingPathComponent:@"Backups"] targetDirNests:YES backupDirNests:NO 
rename:YES]];
+        [backupInfo addObjectsFromArray:[self 
createBackupInfoWithTargetDir:appLibraryFolder backupDir:[appLibraryFolder 
stringByAppendingPathComponent:@"Caches"] targetDirNests:YES backupDirNests:NO 
rename:NO]];
+    } else {
+        assert([backupType isEqualToString:@"local"]);
+        [backupInfo addObjectsFromArray:[self 
createBackupInfoWithTargetDir:[appLibraryFolder 
stringByAppendingPathComponent:@"Caches"] backupDir:appLibraryFolder 
targetDirNests:NO backupDirNests:YES rename:NO]];
+    }
 
     NSFileManager* manager = [NSFileManager defaultManager];
 
@@ -341,6 +358,8 @@
             [manager removeItemAtPath:info.backup error:nil];
         }
     }
+
+    [[NSUserDefaults standardUserDefaults] setBool:(backupType == @"cloud") 
forKey:@"WebKitStoreWebDataForBackup"];
 }
 
 #pragma mark -

http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/a39921be/CordovaLib/Classes/CDVViewController.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVViewController.m 
b/CordovaLib/Classes/CDVViewController.m
index 987ecee..c1648d9 100644
--- a/CordovaLib/Classes/CDVViewController.m
+++ b/CordovaLib/Classes/CDVViewController.m
@@ -175,16 +175,19 @@
 
     // // Fix the iOS 5.1 SECURITY_ERR bug (CB-347), this must be before the 
webView is instantiated ////
 
-    BOOL backupWebStorage = YES;  // default value
-    if ([self.settings objectForKey:@"BackupWebStorage"]) {
-        backupWebStorage = [(NSNumber*)[settings 
objectForKey:@"BackupWebStorage"] boolValue];
-    }
+    NSString* backupWebStorageType = @"cloud"; // default value
 
-    if (backupWebStorage) {
-        [CDVLocalStorage __verifyAndFixDatabaseLocations];
+    id backupWebStorage = [self.settings objectForKey:@"BackupWebStorage"];
+    if ([backupWebStorage isKindOfClass:[NSString class]]) {
+        backupWebStorageType = backupWebStorage;
+    } else if ([backupWebStorage isKindOfClass:[NSNumber class]]) {
+        backupWebStorageType = [(NSNumber*) backupWebStorage boolValue] ? 
@"cloud" : @"none";
     }
+    NSLog(@"BackupType: %@", backupWebStorageType);
 
-    [CDVLocalStorage __fixLegacyDatabaseLocationIssues];
+    if (IsAtLeastiOSVersion(@"5.1")) {
+        [CDVLocalStorage 
__fixupDatabaseLocationsWithBackupType:backupWebStorageType];
+    }
 
     // // Instantiate the WebView ///////////////
 
@@ -211,10 +214,12 @@
     }
 
     /*
-     * Fire up CDVLocalStorage on iOS 5.1+ to work-around WebKit storage 
limitations
+     * Fire up CDVLocalStorage to work-around WebKit storage limitations: on 
all iOS 5.1+ versions for local-only backups, but only needed on iOS 5.1 for 
cloud backup.
      */
-    if (IsAtLeastiOSVersion(@"5.1") && backupWebStorage) {
-        [self registerPlugin:[[CDVLocalStorage alloc] 
initWithWebView:self.webView] withClassName:NSStringFromClass([CDVLocalStorage 
class])];
+    if (IsAtLeastiOSVersion(@"5.1") && (([backupWebStorage 
isEqualToString:@"local"]) ||
+            ([backupWebStorage isEqualToString:@"cloud"] && 
!IsAtLeastiOSVersion(@"6.0")))) {
+        [self registerPlugin:[[CDVLocalStorage alloc] 
initWithWebView:self.webView settings:[NSDictionary 
dictionaryWithObjectsAndKeys:
+                    @"backupType", backupWebStorageType, nil]] 
withClassName:NSStringFromClass([CDVLocalStorage class])];
     }
 
     /*

http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/a39921be/bin/templates/project/__TESTING__/Cordova.plist
----------------------------------------------------------------------
diff --git a/bin/templates/project/__TESTING__/Cordova.plist 
b/bin/templates/project/__TESTING__/Cordova.plist
index 54892bc..2c92742 100644
--- a/bin/templates/project/__TESTING__/Cordova.plist
+++ b/bin/templates/project/__TESTING__/Cordova.plist
@@ -45,7 +45,7 @@
        <key>OpenAllWhitelistURLsInWebView</key>
        <false/>
        <key>BackupWebStorage</key>
-       <true/>
+       <string>gray</string>
        <key>ExternalHosts</key>
        <array/>
        <key>Plugins</key>

Reply via email to