From d442e29e22e7cdb8a36f2bf3d2ad7cfc8910a0e9 Mon Sep 17 00:00:00 2001 From: Bibiko Date: Mon, 20 Dec 2010 09:48:22 +0000 Subject: =?UTF-8?q?=E2=80=A2=20preparations=20for=20managing=20default=20B?= =?UTF-8?q?undles=20which=20will=20ship=20with=20SP=20=E2=80=A2=20improved?= =?UTF-8?q?=20Bundle=20Editor=20to=20store=20only=20modified=20Bundles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Resources/Plists/PreferenceDefaults.plist | 4 + Source/SPAppController.m | 267 ++++++++++++++++++++---------- Source/SPBundleEditorController.m | 32 ++++ Source/SPConstants.h | 2 + Source/SPConstants.m | 2 + 5 files changed, 222 insertions(+), 85 deletions(-) diff --git a/Resources/Plists/PreferenceDefaults.plist b/Resources/Plists/PreferenceDefaults.plist index 093174a2..591bb967 100644 --- a/Resources/Plists/PreferenceDefaults.plist +++ b/Resources/Plists/PreferenceDefaults.plist @@ -175,5 +175,9 @@ LIKE '%@%' WebKitDeveloperExtras + deletedDefaultBundles + + updatedDefaultBundles + diff --git a/Source/SPAppController.m b/Source/SPAppController.m index 046628b3..ec315f78 100644 --- a/Source/SPAppController.m +++ b/Source/SPAppController.m @@ -1318,117 +1318,214 @@ // Clean menu [menu compatibleRemoveAllItems]; - NSString *bundlePath = [[NSFileManager defaultManager] applicationSupportDirectoryForSubDirectory:SPBundleSupportFolder createIfNotExists:NO error:nil]; + NSArray *bundlePaths = [NSArray arrayWithObjects: + ([[NSFileManager defaultManager] applicationSupportDirectoryForSubDirectory:SPBundleSupportFolder createIfNotExists:NO error:nil])?:@"", + [NSString stringWithFormat:@"%@/Contents/Resources/Default Bundles", [[NSBundle mainBundle] bundlePath]], + nil]; - if(bundlePath) { - NSError *error = nil; - NSArray *foundBundles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:bundlePath error:&error]; - if (foundBundles && [foundBundles count] && error == nil) { - for(NSString* bundle in foundBundles) { - if(![[[bundle pathExtension] lowercaseString] isEqualToString:[SPUserBundleFileExtension lowercaseString]]) continue; + BOOL processDefaultBundles = NO; + NSFileManager *fm = [NSFileManager defaultManager]; + + NSArray *deletedDefaultBundles; + NSMutableArray *updatedDefaultBundles = [NSMutableArray array]; + if([[NSUserDefaults standardUserDefaults] objectForKey:@"deletedDefaultBundles"]) { + deletedDefaultBundles = [[[NSUserDefaults standardUserDefaults] objectForKey:@"deletedDefaultBundles"] retain]; + } else { + deletedDefaultBundles = [[NSArray array] retain]; + } + if([[NSUserDefaults standardUserDefaults] objectForKey:@"updatedDefaultBundles"]) { + [updatedDefaultBundles setArray:[[NSUserDefaults standardUserDefaults] objectForKey:@"updatedDefaultBundles"]]; + } - foundInstalledBundles = YES; + for(NSString* bundlePath in bundlePaths) { + if([bundlePath length]) { - NSError *readError = nil; - NSString *convError = nil; - NSPropertyListFormat format; - NSDictionary *cmdData = nil; - NSString *infoPath = [NSString stringWithFormat:@"%@/%@/%@", bundlePath, bundle, SPBundleFileName]; - NSData *pData = [NSData dataWithContentsOfFile:infoPath options:NSUncachedRead error:&readError]; + NSError *error = nil; + NSArray *foundBundles = [fm contentsOfDirectoryAtPath:bundlePath error:&error]; + if (foundBundles && [foundBundles count] && error == nil) { - cmdData = [[NSPropertyListSerialization propertyListFromData:pData - mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError] retain]; + for(NSString* bundle in foundBundles) { + if(![[[bundle pathExtension] lowercaseString] isEqualToString:[SPUserBundleFileExtension lowercaseString]]) continue; - if(!cmdData || readError != nil || [convError length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) { - NSLog(@"“%@/%@” file couldn't be read.", bundle, SPBundleFileName); - NSBeep(); - if (cmdData) [cmdData release]; - } else { - if((![cmdData objectForKey:SPBundleFileDisabledKey] || ![[cmdData objectForKey:SPBundleFileDisabledKey] intValue]) && [cmdData objectForKey:SPBundleFileNameKey] && [[cmdData objectForKey:SPBundleFileNameKey] length] && [cmdData objectForKey:SPBundleFileScopeKey]) - { + foundInstalledBundles = YES; + + NSError *readError = nil; + NSString *convError = nil; + NSPropertyListFormat format; + NSDictionary *cmdData = nil; + NSString *infoPath = [NSString stringWithFormat:@"%@/%@/%@", bundlePath, bundle, SPBundleFileName]; + NSData *pData = [NSData dataWithContentsOfFile:infoPath options:NSUncachedRead error:&readError]; + + cmdData = [[NSPropertyListSerialization propertyListFromData:pData + mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError] retain]; - NSArray *scopes = [[cmdData objectForKey:SPBundleFileScopeKey] componentsSeparatedByString:@" "]; - for(NSString *scope in scopes) { - if(![bundleUsedScopes containsObject:scope]) { - [bundleUsedScopes addObject:scope]; - [bundleItems setObject:[NSMutableArray array] forKey:scope]; - [bundleCategories setObject:[NSMutableArray array] forKey:scope]; - [bundleKeyEquivalents setObject:[NSMutableDictionary dictionary] forKey:scope]; + if(!cmdData || readError != nil || [convError length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) { + + NSLog(@"“%@” file couldn't be read.", infoPath); + NSBeep(); + + } else { + if((![cmdData objectForKey:SPBundleFileDisabledKey] || ![[cmdData objectForKey:SPBundleFileDisabledKey] intValue]) + && [cmdData objectForKey:SPBundleFileNameKey] + && [[cmdData objectForKey:SPBundleFileNameKey] length] + && [cmdData objectForKey:SPBundleFileScopeKey]) + { + + if([cmdData objectForKey:SPBundleFileUUIDKey] && [[cmdData objectForKey:SPBundleFileUUIDKey] length]) { + + if(processDefaultBundles) { + + // Skip deleted default Bundles + if([deletedDefaultBundles containsObject:[cmdData objectForKey:SPBundleFileUUIDKey]]) + continue; + + // If default Bundle is already install check for possible update, + // if so duplicate the 'old one' by renaming it and change the UUID + if([installedBundleUUIDs objectForKey:[cmdData objectForKey:SPBundleFileUUIDKey]]) { + if([updatedDefaultBundles containsObject:[cmdData objectForKey:SPBundleFileUUIDKey]]) { + NSString *oldPath = [NSString stringWithFormat:@"%@/%@/%@", [bundlePaths objectAtIndex:0], bundle, SPBundleFileName]; + NSError *readError = nil; + NSString *convError = nil; + NSPropertyListFormat format; + NSDictionary *cmdData = nil; + NSData *pData = [NSData dataWithContentsOfFile:oldPath options:NSUncachedRead error:&readError]; + cmdData = [[NSPropertyListSerialization propertyListFromData:pData + mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError] retain]; + if(!cmdData || readError != nil || [convError length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) { + NSLog(@"“%@” file couldn't be read.", oldPath); + NSBeep(); + continue; + } else { + // Check for modifications + if([cmdData objectForKey:SPBundleFileDefaultBundleWasModifiedKey]) { + + } else { + [fm removeItemAtPath:[NSString stringWithFormat:@"%@/%@", [bundlePaths objectAtIndex:0], bundle]]; + [updatedDefaultBundles removeObject:[cmdData objectForKey:SPBundleFileUUIDKey]]; + } + } + } else { + continue; + } + } + + BOOL isDir; + NSString *newInfoPath = [NSString stringWithFormat:@"%@/%@/%@", [bundlePaths objectAtIndex:0], bundle, SPBundleFileName]; + if([fm fileExistsAtPath:newInfoPath isDirectory:&isDir] && isDir) + newInfoPath = [NSString stringWithFormat:@"%@_%ld", newInfoPath, (NSUInteger)(random() % 35000)]; + NSError *error = nil; + [fm moveItemAtPath:infoPath toPath:newInfoPath error:&error]; + if(error != nil) { + NSBeep(); + NSLog(@"Default Bundle “%@” couldn't be moved to '%@'", bundle, newInfoPath); + continue; + } + infoPath = [NSString stringWithString:newInfoPath]; + + } + + [installedBundleUUIDs setObject:[NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithFormat:@"%@ (%@)", bundle, [cmdData objectForKey:SPBundleFileNameKey]], @"name", + infoPath, @"path", nil] forKey:[cmdData objectForKey:SPBundleFileUUIDKey]]; + + } else { + NSLog(@"No UUID for %@", bundle); + NSBeep(); + continue; } - if([cmdData objectForKey:SPBundleFileCategoryKey] && [[cmdData objectForKey:SPBundleFileCategoryKey] length] && ![[bundleCategories objectForKey:scope] containsObject:[cmdData objectForKey:SPBundleFileCategoryKey]]) - [[bundleCategories objectForKey:scope] addObject:[cmdData objectForKey:SPBundleFileCategoryKey]]; - } + NSArray *scopes = [[cmdData objectForKey:SPBundleFileScopeKey] componentsSeparatedByString:@" "]; + for(NSString *scope in scopes) { - NSMutableDictionary *aDict = [NSMutableDictionary dictionary]; - [aDict setObject:[cmdData objectForKey:SPBundleFileNameKey] forKey:SPBundleInternLabelKey]; - [aDict setObject:infoPath forKey:SPBundleInternPathToFileKey]; + if(![bundleUsedScopes containsObject:scope]) { + [bundleUsedScopes addObject:scope]; + [bundleItems setObject:[NSMutableArray array] forKey:scope]; + [bundleCategories setObject:[NSMutableArray array] forKey:scope]; + [bundleKeyEquivalents setObject:[NSMutableDictionary dictionary] forKey:scope]; + } - // Register trigger - if([cmdData objectForKey:SPBundleFileTriggerKey]) { - if(![bundleTriggers objectForKey:[cmdData objectForKey:SPBundleFileTriggerKey]]) - [bundleTriggers setObject:[NSMutableArray array] forKey:[cmdData objectForKey:SPBundleFileTriggerKey]]; - [[bundleTriggers objectForKey:[cmdData objectForKey:SPBundleFileTriggerKey]] addObject:[NSString stringWithFormat:@"%@|%@|%@", infoPath, [cmdData objectForKey:SPBundleFileScopeKey], ([[cmdData objectForKey:SPBundleFileOutputActionKey] isEqualToString:SPBundleOutputActionShowAsHTML])?[cmdData objectForKey:SPBundleFileUUIDKey]:@""]]; - } + if([cmdData objectForKey:SPBundleFileCategoryKey] && [[cmdData objectForKey:SPBundleFileCategoryKey] length] && ![[bundleCategories objectForKey:scope] containsObject:[cmdData objectForKey:SPBundleFileCategoryKey]]) + [[bundleCategories objectForKey:scope] addObject:[cmdData objectForKey:SPBundleFileCategoryKey]]; + } + + NSMutableDictionary *aDict = [NSMutableDictionary dictionary]; + [aDict setObject:[cmdData objectForKey:SPBundleFileNameKey] forKey:SPBundleInternLabelKey]; + [aDict setObject:infoPath forKey:SPBundleInternPathToFileKey]; + + // Register trigger + if([cmdData objectForKey:SPBundleFileTriggerKey]) { + if(![bundleTriggers objectForKey:[cmdData objectForKey:SPBundleFileTriggerKey]]) + [bundleTriggers setObject:[NSMutableArray array] forKey:[cmdData objectForKey:SPBundleFileTriggerKey]]; + [[bundleTriggers objectForKey:[cmdData objectForKey:SPBundleFileTriggerKey]] addObject: + [NSString stringWithFormat:@"%@|%@|%@", + infoPath, + [cmdData objectForKey:SPBundleFileScopeKey], + ([[cmdData objectForKey:SPBundleFileOutputActionKey] isEqualToString:SPBundleOutputActionShowAsHTML])?[cmdData objectForKey:SPBundleFileUUIDKey]:@""]]; + } + + if([cmdData objectForKey:SPBundleFileKeyEquivalentKey] && [[cmdData objectForKey:SPBundleFileKeyEquivalentKey] length]) { + + NSString *theKey = [cmdData objectForKey:SPBundleFileKeyEquivalentKey]; + NSString *theChar = [theKey substringFromIndex:[theKey length]-1]; + NSString *theMods = [theKey substringToIndex:[theKey length]-1]; + NSUInteger mask = 0; + if([theMods rangeOfString:@"^"].length) + mask = mask | NSControlKeyMask; + if([theMods rangeOfString:@"@"].length) + mask = mask | NSCommandKeyMask; + if([theMods rangeOfString:@"~"].length) + mask = mask | NSAlternateKeyMask; + if([theMods rangeOfString:@"$"].length) + mask = mask | NSShiftKeyMask; + for(NSString* scope in scopes) { + if(![[bundleKeyEquivalents objectForKey:scope] objectForKey:[cmdData objectForKey:SPBundleFileKeyEquivalentKey]]) + [[bundleKeyEquivalents objectForKey:scope] setObject:[NSMutableArray array] forKey:[cmdData objectForKey:SPBundleFileKeyEquivalentKey]]; + + [[[bundleKeyEquivalents objectForKey:scope] objectForKey:[cmdData objectForKey:SPBundleFileKeyEquivalentKey]] addObject: + [NSDictionary dictionaryWithObjectsAndKeys: + infoPath, @"path", + [cmdData objectForKey:SPBundleFileNameKey], @"title", + ([cmdData objectForKey:SPBundleFileTooltipKey]) ?: @"", @"tooltip", + nil]]; - if([cmdData objectForKey:SPBundleFileKeyEquivalentKey] && [[cmdData objectForKey:SPBundleFileKeyEquivalentKey] length]) { - NSString *theKey = [cmdData objectForKey:SPBundleFileKeyEquivalentKey]; - NSString *theChar = [theKey substringFromIndex:[theKey length]-1]; - NSString *theMods = [theKey substringToIndex:[theKey length]-1]; - NSUInteger mask = 0; - if([theMods rangeOfString:@"^"].length) - mask = mask | NSControlKeyMask; - if([theMods rangeOfString:@"@"].length) - mask = mask | NSCommandKeyMask; - if([theMods rangeOfString:@"~"].length) - mask = mask | NSAlternateKeyMask; - if([theMods rangeOfString:@"$"].length) - mask = mask | NSShiftKeyMask; - for(NSString* scope in scopes) { - if(![[bundleKeyEquivalents objectForKey:scope] objectForKey:[cmdData objectForKey:SPBundleFileKeyEquivalentKey]]) - [[bundleKeyEquivalents objectForKey:scope] setObject:[NSMutableArray array] forKey:[cmdData objectForKey:SPBundleFileKeyEquivalentKey]]; - - [[[bundleKeyEquivalents objectForKey:scope] objectForKey:[cmdData objectForKey:SPBundleFileKeyEquivalentKey]] addObject: - [NSDictionary dictionaryWithObjectsAndKeys: - infoPath, @"path", - [cmdData objectForKey:SPBundleFileNameKey], @"title", - ([cmdData objectForKey:SPBundleFileTooltipKey]) ?: @"", @"tooltip", - nil]]; + } + [aDict setObject:[NSArray arrayWithObjects:theChar, [NSNumber numberWithInteger:mask], nil] forKey:SPBundleInternKeyEquivalentKey]; } - [aDict setObject:[NSArray arrayWithObjects:theChar, [NSNumber numberWithInteger:mask], nil] forKey:SPBundleInternKeyEquivalentKey]; - } + if([cmdData objectForKey:SPBundleFileTooltipKey] && [[cmdData objectForKey:SPBundleFileTooltipKey] length]) + [aDict setObject:[cmdData objectForKey:SPBundleFileTooltipKey] forKey:SPBundleFileTooltipKey]; - if([cmdData objectForKey:SPBundleFileUUIDKey] && [[cmdData objectForKey:SPBundleFileUUIDKey] length]) - [installedBundleUUIDs setObject:[NSDictionary dictionaryWithObjectsAndKeys: - [NSString stringWithFormat:@"%@ (%@)", bundle, [cmdData objectForKey:SPBundleFileNameKey]], @"name", - infoPath, @"path", nil] forKey:[cmdData objectForKey:SPBundleFileUUIDKey]]; + if([cmdData objectForKey:SPBundleFileCategoryKey] && [[cmdData objectForKey:SPBundleFileCategoryKey] length]) + [aDict setObject:[cmdData objectForKey:SPBundleFileCategoryKey] forKey:SPBundleFileCategoryKey]; - if([cmdData objectForKey:SPBundleFileTooltipKey] && [[cmdData objectForKey:SPBundleFileTooltipKey] length]) - [aDict setObject:[cmdData objectForKey:SPBundleFileTooltipKey] forKey:SPBundleFileTooltipKey]; + if([cmdData objectForKey:SPBundleFileKeyEquivalentKey] && [[cmdData objectForKey:SPBundleFileKeyEquivalentKey] length]) + [aDict setObject:[cmdData objectForKey:SPBundleFileKeyEquivalentKey] forKey:@"key"]; - if([cmdData objectForKey:SPBundleFileCategoryKey] && [[cmdData objectForKey:SPBundleFileCategoryKey] length]) - [aDict setObject:[cmdData objectForKey:SPBundleFileCategoryKey] forKey:SPBundleFileCategoryKey]; + for(NSString* scope in scopes) + [[bundleItems objectForKey:scope] addObject:aDict]; - if([cmdData objectForKey:SPBundleFileKeyEquivalentKey] && [[cmdData objectForKey:SPBundleFileKeyEquivalentKey] length]) - [aDict setObject:[cmdData objectForKey:SPBundleFileKeyEquivalentKey] forKey:@"key"]; + } + + if (cmdData) [cmdData release]; - for(NSString* scope in scopes) - [[bundleItems objectForKey:scope] addObject:aDict]; } - if (cmdData) [cmdData release]; } - } - NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:SPBundleInternLabelKey ascending:YES] autorelease]; - for(NSString* scope in [bundleItems allKeys]) { - [[bundleItems objectForKey:scope] sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]]; - [[bundleCategories objectForKey:scope] sortUsingSelector:@selector(compare:)]; + NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:SPBundleInternLabelKey ascending:YES] autorelease]; + for(NSString* scope in [bundleItems allKeys]) { + [[bundleItems objectForKey:scope] sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]]; + [[bundleCategories objectForKey:scope] sortUsingSelector:@selector(compare:)]; + } } } + processDefaultBundles = YES; } + [deletedDefaultBundles release]; + + [[NSUserDefaults standardUserDefaults] setObject:updatedDefaultBundles forKey:@"updatedDefaultBundles"]; + // Rebuild Bundles main menu item // Add default menu items diff --git a/Source/SPBundleEditorController.m b/Source/SPBundleEditorController.m index 32f5b581..82c93047 100644 --- a/Source/SPBundleEditorController.m +++ b/Source/SPBundleEditorController.m @@ -871,6 +871,7 @@ NSFileManager *fm = [NSFileManager defaultManager]; BOOL isDir = NO; + BOOL isNewBundle = NO; // If passed aPath is nil construct the path from bundle's bundleName. // aPath is mainly used for dragging a bundle from table view. @@ -888,6 +889,7 @@ if(![fm createDirectoryAtPath:aPath withIntermediateDirectories:YES attributes:nil error:nil]) return NO; isDir = YES; + isNewBundle = YES; } // If aPath exists but it's not a folder bail out @@ -908,6 +910,28 @@ kBundleNameKey, nil]]; + + if(!isNewBundle) { + NSError *readError = nil; + NSString *convError = nil; + NSPropertyListFormat format; + NSDictionary *cmdData = nil; + NSData *pData = [NSData dataWithContentsOfFile:cmdFilePath options:NSUncachedRead error:&readError]; + cmdData = [[NSPropertyListSerialization propertyListFromData:pData + mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError] retain]; + if(!cmdData || readError != nil || [convError length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) { + NSLog(@"“%@” file couldn't be read.", cmdFilePath); + NSBeep(); + return NO; + } else { + // Check for changes and return if no changes are found + if([[saveDict description] isEqualToString:[cmdData description]]) + return YES; + if([cmdData objectForKey:SPBundleFileIsDefaultBundleKey]) + [saveDict setObject:[NSNumber numberWithBool:YES] forKey:SPBundleFileDefaultBundleWasModifiedKey]; + } + } + // Remove a given old command.plist file [fm removeItemAtPath:cmdFilePath error:nil]; [saveDict writeToFile:cmdFilePath atomically:YES]; @@ -960,6 +984,14 @@ deletionSuccessfully = NO; break; } + if([obj objectForKey:SPBundleFileIsDefaultBundleKey]) { + NSMutableArray *deletedBundles = [NSMutableArray array]; + [deletedBundles setArray:[[NSUserDefaults standardUserDefaults] objectForKey:@"deletedDefaultBundles"]]; + if(![deletedBundles containsObject:[obj objectForKey:SPBundleFileUUIDKey]]) { + [deletedBundles addObject:[obj objectForKey:SPBundleFileUUIDKey]]; + [[NSUserDefaults standardUserDefaults] setObject:deletedBundles forKey:@"deletedDefaultBundles"]; + } + } [commandsOutlineView reloadData]; } } diff --git a/Source/SPConstants.h b/Source/SPConstants.h index 7d85d2b7..37687ba1 100644 --- a/Source/SPConstants.h +++ b/Source/SPConstants.h @@ -482,6 +482,8 @@ extern NSString *SPBundleFileUUIDKey; extern NSString *SPBundleFileDescriptionKey; extern NSString *SPBundleFileTriggerKey; extern NSString *SPBundleFileWithBlobKey; +extern NSString *SPBundleFileIsDefaultBundleKey; +extern NSString *SPBundleFileDefaultBundleWasModifiedKey; extern NSString *SPBundleInternLabelKey; extern NSString *SPBundleInternPathToFileKey; extern NSString *SPBundleInternKeyEquivalentKey; diff --git a/Source/SPConstants.m b/Source/SPConstants.m index d118b6b6..d2edebfa 100644 --- a/Source/SPConstants.m +++ b/Source/SPConstants.m @@ -294,6 +294,8 @@ NSString *SPBundleFileUUIDKey = @"uuid"; NSString *SPBundleFileDescriptionKey = @"description"; NSString *SPBundleFileTriggerKey = @"trigger"; NSString *SPBundleFileWithBlobKey = @"withblob"; +NSString *SPBundleFileIsDefaultBundleKey = @"isDefaultBundle"; +NSString *SPBundleFileDefaultBundleWasModifiedKey = @"defaultBundleWasModified"; NSString *SPBundleInternLabelKey = @"label"; NSString *SPBundleInternPathToFileKey = @"path"; NSString *SPBundleInternKeyEquivalentKey = @"keyEquivalent"; -- cgit v1.2.3