Skip to content

Commit

Permalink
Support installing bundled plugins from files
Browse files Browse the repository at this point in the history
  • Loading branch information
nate-parrott committed Jan 19, 2015
1 parent 7359e3d commit d337e8d
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 38 deletions.
31 changes: 28 additions & 3 deletions FlashlightApp/EasySIMBL/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#import "PluginModel.h"
#import <LetsMove/PFMoveApplication.h>
#import "UpdateChecker.h"
#import "PluginInstallTask.h"

@interface AppDelegate ()

Expand Down Expand Up @@ -145,8 +146,33 @@ - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theAppl

- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
{
// TODO: install the plugin
[sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
BOOL anyFilesMatch = NO;
for (NSString *filename in filenames) {
if ([filename.pathExtension isEqualToString:@"flashlightplugin"]) {
PluginInstallTask *task = [PluginInstallTask new];
[task installPluginData:[NSData dataWithContentsOfFile:filename] intoPluginsDirectory:[PluginModel pluginsDir] callback:^(BOOL success, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (success) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.pluginListController showInstalledPluginWithName:task.installedPluginName];
});
} else {
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:NSLocalizedString(@"Couldn't Install Plugin", @"")];
[alert addButtonWithTitle:NSLocalizedString(@"Okay", @"")]; // FirstButton, rightmost button
[alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"This file doesn't appear to be a valid plugin.", @"")]];
alert.alertStyle = NSCriticalAlertStyle;
[alert runModal];
}
});
}];
}
}
if (anyFilesMatch) {
[sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
} else {
[sender replyToOpenOrPrint:NSApplicationDelegateReplyFailure];
}
}

#pragma mark NSKeyValueObserving Protocol
Expand Down Expand Up @@ -265,5 +291,4 @@ - (void)handleURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEv
}
}
}

@end
8 changes: 7 additions & 1 deletion FlashlightApp/EasySIMBL/Flashlight-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@
<array>
<string>flashlightplugin</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>GenericPluginIcon</string>
<key>CFBundleTypeName</key>
<string>Flashlight Spotlight Plugin</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>com.nateparrott.Flashlight.Plugin</string>
</array>
<key>LSTypeIsPackage</key>
<true/>
<integer>0</integer>
</dict>
</array>
<key>CFBundleExecutable</key>
Expand Down
Binary file added FlashlightApp/EasySIMBL/GenericPluginIcon.icns
Binary file not shown.
2 changes: 2 additions & 0 deletions FlashlightApp/EasySIMBL/PluginInstallTask.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

- (id)initWithPlugin:(PluginModel *)plugin;
- (void)startInstallationIntoPluginsDirectory:(NSString *)directory withCallback:(void(^)(BOOL success, NSError *error))callback; // callback comes on arbitrary thread
- (void)installPluginData:(NSData *)data intoPluginsDirectory:(NSString *)directory callback:(void(^)(BOOL success, NSError *error))callback;
@property (nonatomic,readonly) PluginModel *plugin;
@property (nonatomic,readonly) NSString *installedPluginName;

@end
95 changes: 61 additions & 34 deletions FlashlightApp/EasySIMBL/PluginInstallTask.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,45 +26,72 @@ - (void)startInstallationIntoPluginsDirectory:(NSString *)directory withCallback
if (self.plugin.zipURL) {
[[[NSURLSession sharedSession] dataTaskWithURL:self.plugin.zipURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (data && !error) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSError *zipError = nil;
ZZArchive *archive = [ZZArchive archiveWithData:data error:&zipError];
if (archive && !zipError) {
for (ZZArchiveEntry *entry in archive.entries) {
zipError = nil;
NSData *entryData = [entry newDataWithError:&zipError];
if (entryData && !zipError) {
NSString *writeToPath = [directory stringByAppendingPathComponent:entry.fileName];
if (![[NSFileManager defaultManager] fileExistsAtPath:[writeToPath stringByDeletingLastPathComponent]]) {
[[NSFileManager defaultManager] createDirectoryAtPath:[writeToPath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:NO];
}
if ([[writeToPath pathExtension] isEqualToString:@"bundle"]) {
continue;
}
[entryData writeToFile:writeToPath atomically:YES];
} else {
callback(NO, zipError);
return;
}
}
// done:
if (self.plugin.openPreferencesOnInstall) {
dispatch_async(dispatch_get_main_queue(), ^{
AppDelegate *d = (id)[NSApp delegate];
[self.plugin presentOptionsInWindow:d.window];
});
}
[[UpdateChecker shared] justInstalledPlugin:self.plugin.name];
callback(YES, nil);
} else {
callback(NO, zipError);
}
});
[self installPluginData:data intoPluginsDirectory:directory callback:callback];
} else {
callback(NO, error);
}
}] resume];
}
}

- (NSString *)nameForPluginContainingPath:(NSString *)path {
for (NSString *comp in path.pathComponents.reverseObjectEnumerator) {
if ([comp.pathExtension isEqualToString:@"bundle"]) {
return comp.stringByDeletingPathExtension;
}
}
return nil;
}

- (void)installPluginData:(NSData *)data intoPluginsDirectory:(NSString *)directory callback:(void(^)(BOOL success, NSError *error))callback {
if (!data) {
callback(NO, nil);
return;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSString *pluginName = nil;
NSError *zipError = nil;
ZZArchive *archive = [ZZArchive archiveWithData:data error:&zipError];
if (archive && !zipError) {
for (ZZArchiveEntry *entry in archive.entries) {
zipError = nil;
NSData *entryData = [entry newDataWithError:&zipError];
if (entryData && !zipError) {
NSString *writeToPath = [directory stringByAppendingPathComponent:entry.fileName];
if (![[NSFileManager defaultManager] fileExistsAtPath:[writeToPath stringByDeletingLastPathComponent]]) {
[[NSFileManager defaultManager] createDirectoryAtPath:[writeToPath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:NO];
}
if ([[writeToPath pathExtension] isEqualToString:@"bundle"]) {
continue;
}
if (!pluginName && [self nameForPluginContainingPath:writeToPath]) {
pluginName = [self nameForPluginContainingPath:writeToPath];
}
[entryData writeToFile:writeToPath atomically:YES];
} else {
callback(NO, zipError);
return;
}
}
if (!pluginName) {
callback(NO, nil);
return;
}
_installedPluginName = pluginName;
PluginModel *pluginModel = [PluginModel installedPluginNamed:pluginName];
// done:
if (pluginModel.openPreferencesOnInstall) {
dispatch_async(dispatch_get_main_queue(), ^{
AppDelegate *d = (id)[NSApp delegate];
[pluginModel presentOptionsInWindow:d.window];
});
}
[[UpdateChecker shared] justInstalledPlugin:self.plugin.name];
callback(YES, nil);
} else {
callback(NO, zipError);
}
});
}

@end
1 change: 1 addition & 0 deletions FlashlightApp/EasySIMBL/PluginListController.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
@property (nonatomic) IBOutlet NSOutlineView *sourceList;

- (void)showPluginWithName:(NSString *)name;
- (void)showInstalledPluginWithName:(NSString *)name;

@property (nonatomic) BOOL enabled;

Expand Down
12 changes: 12 additions & 0 deletions FlashlightApp/EasySIMBL/PluginListController.m
Original file line number Diff line number Diff line change
Expand Up @@ -601,4 +601,16 @@ - (void)showPluginWithName:(NSString *)name {
self.selectedCategory = kCategoryShowIndividualPlugin;
}

- (void)showInstalledPluginWithName:(NSString *)name {
self.selectedCategory = @"Installed";
self.selectedPluginName = name;
PluginModel *model = [self.installedPlugins map:^id(id obj) {
return [[obj name] isEqualToString:name] ? obj : nil;
}].firstObject;
NSUInteger index = model ? [self.installedPlugins indexOfObject:model] : NSNotFound;
if (index != NSNotFound) {
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];
}
}

@end
4 changes: 4 additions & 0 deletions FlashlightApp/Flashlight.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
5FD3EA841A204CA1005A312E /* NSURLComponents+ValueForQueryKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FD3EA831A204CA1005A312E /* NSURLComponents+ValueForQueryKey.m */; };
5FF5D4111A675F0800641D7A /* UpdateChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FF5D4101A675F0800641D7A /* UpdateChecker.m */; };
5FF873161A6D71EA00EAD72F /* NSObject+InternationalizedValueForKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FF873151A6D71EA00EAD72F /* NSObject+InternationalizedValueForKey.m */; };
5FF8731D1A6D977500EAD72F /* GenericPluginIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 5FF8731C1A6D977500EAD72F /* GenericPluginIcon.icns */; };
6C3A4C44159EADF900985CCD /* FlashlightSIMBLAgent.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 6C3A4C35159E7E7000985CCD /* FlashlightSIMBLAgent.app */; };
6C564485159E7C0800215467 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C564484159E7C0800215467 /* Cocoa.framework */; };
6C56448F159E7C0800215467 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6C56448D159E7C0800215467 /* InfoPlist.strings */; };
Expand Down Expand Up @@ -204,6 +205,7 @@
5FF5D4101A675F0800641D7A /* UpdateChecker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UpdateChecker.m; sourceTree = "<group>"; };
5FF873141A6D71EA00EAD72F /* NSObject+InternationalizedValueForKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+InternationalizedValueForKey.h"; path = "../FlashlightKit/FlashlightKit/NSObject+InternationalizedValueForKey.h"; sourceTree = "<group>"; };
5FF873151A6D71EA00EAD72F /* NSObject+InternationalizedValueForKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+InternationalizedValueForKey.m"; path = "../FlashlightKit/FlashlightKit/NSObject+InternationalizedValueForKey.m"; sourceTree = "<group>"; };
5FF8731C1A6D977500EAD72F /* GenericPluginIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = GenericPluginIcon.icns; sourceTree = "<group>"; };
653963C51A2766CD00D8F6E9 /* libPods-Flashlight.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libPods-Flashlight.a"; path = "../../../../../Library/Developer/Xcode/DerivedData/Pods-gpoaxpycjinulnbzpvilkgtvohcj/Build/Products/Debug/libPods-Flashlight.a"; sourceTree = "<group>"; };
658988FB1A270ACF00ABE5E9 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
6C3A4C2B159E7E6F00985CCD /* SIMBL.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = SIMBL.xcodeproj; sourceTree = "<group>"; };
Expand Down Expand Up @@ -461,6 +463,7 @@
6C56448B159E7C0800215467 /* Supporting Files */ = {
isa = PBXGroup;
children = (
5FF8731C1A6D977500EAD72F /* GenericPluginIcon.icns */,
5FC0166E1A1DBE7300F8E4E7 /* Localizable.strings */,
5F6326C31A083E1F00CE241E /* Icon.icns */,
6C56448C159E7C0800215467 /* Flashlight-Info.plist */,
Expand Down Expand Up @@ -562,6 +565,7 @@
files = (
5FBEC9C41A166C00007FEC54 /* dsa_pub.pem in Resources */,
6C56448F159E7C0800215467 /* InfoPlist.strings in Resources */,
5FF8731D1A6D977500EAD72F /* GenericPluginIcon.icns in Resources */,
5F60B0FE1A196562004BCF08 /* WorkflowTemplate.bundle in Resources */,
5F2B37761A3E42E500AC2C19 /* SearchPluginEditorWindowController.xib in Resources */,
5F6326C41A083E1F00CE241E /* Icon.icns in Resources */,
Expand Down

0 comments on commit d337e8d

Please sign in to comment.