Skip to content

Commit d337e8d

Browse files
committed
Support installing bundled plugins from files
1 parent 7359e3d commit d337e8d

File tree

8 files changed

+115
-38
lines changed

8 files changed

+115
-38
lines changed

FlashlightApp/EasySIMBL/AppDelegate.m

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#import "PluginModel.h"
1313
#import <LetsMove/PFMoveApplication.h>
1414
#import "UpdateChecker.h"
15+
#import "PluginInstallTask.h"
1516

1617
@interface AppDelegate ()
1718

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

146147
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
147148
{
148-
// TODO: install the plugin
149-
[sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
149+
BOOL anyFilesMatch = NO;
150+
for (NSString *filename in filenames) {
151+
if ([filename.pathExtension isEqualToString:@"flashlightplugin"]) {
152+
PluginInstallTask *task = [PluginInstallTask new];
153+
[task installPluginData:[NSData dataWithContentsOfFile:filename] intoPluginsDirectory:[PluginModel pluginsDir] callback:^(BOOL success, NSError *error) {
154+
dispatch_async(dispatch_get_main_queue(), ^{
155+
if (success) {
156+
dispatch_async(dispatch_get_main_queue(), ^{
157+
[self.pluginListController showInstalledPluginWithName:task.installedPluginName];
158+
});
159+
} else {
160+
NSAlert *alert = [[NSAlert alloc] init];
161+
[alert setMessageText:NSLocalizedString(@"Couldn't Install Plugin", @"")];
162+
[alert addButtonWithTitle:NSLocalizedString(@"Okay", @"")]; // FirstButton, rightmost button
163+
[alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(@"This file doesn't appear to be a valid plugin.", @"")]];
164+
alert.alertStyle = NSCriticalAlertStyle;
165+
[alert runModal];
166+
}
167+
});
168+
}];
169+
}
170+
}
171+
if (anyFilesMatch) {
172+
[sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
173+
} else {
174+
[sender replyToOpenOrPrint:NSApplicationDelegateReplyFailure];
175+
}
150176
}
151177

152178
#pragma mark NSKeyValueObserving Protocol
@@ -265,5 +291,4 @@ - (void)handleURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEv
265291
}
266292
}
267293
}
268-
269294
@end

FlashlightApp/EasySIMBL/Flashlight-Info.plist

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@
1111
<array>
1212
<string>flashlightplugin</string>
1313
</array>
14+
<key>CFBundleTypeIconFile</key>
15+
<string>GenericPluginIcon</string>
1416
<key>CFBundleTypeName</key>
1517
<string>Flashlight Spotlight Plugin</string>
1618
<key>CFBundleTypeRole</key>
1719
<string>Viewer</string>
20+
<key>LSItemContentTypes</key>
21+
<array>
22+
<string>com.nateparrott.Flashlight.Plugin</string>
23+
</array>
1824
<key>LSTypeIsPackage</key>
19-
<true/>
25+
<integer>0</integer>
2026
</dict>
2127
</array>
2228
<key>CFBundleExecutable</key>
252 KB
Binary file not shown.

FlashlightApp/EasySIMBL/PluginInstallTask.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

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

1820
@end

FlashlightApp/EasySIMBL/PluginInstallTask.m

Lines changed: 61 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,45 +26,72 @@ - (void)startInstallationIntoPluginsDirectory:(NSString *)directory withCallback
2626
if (self.plugin.zipURL) {
2727
[[[NSURLSession sharedSession] dataTaskWithURL:self.plugin.zipURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
2828
if (data && !error) {
29-
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
30-
NSError *zipError = nil;
31-
ZZArchive *archive = [ZZArchive archiveWithData:data error:&zipError];
32-
if (archive && !zipError) {
33-
for (ZZArchiveEntry *entry in archive.entries) {
34-
zipError = nil;
35-
NSData *entryData = [entry newDataWithError:&zipError];
36-
if (entryData && !zipError) {
37-
NSString *writeToPath = [directory stringByAppendingPathComponent:entry.fileName];
38-
if (![[NSFileManager defaultManager] fileExistsAtPath:[writeToPath stringByDeletingLastPathComponent]]) {
39-
[[NSFileManager defaultManager] createDirectoryAtPath:[writeToPath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:NO];
40-
}
41-
if ([[writeToPath pathExtension] isEqualToString:@"bundle"]) {
42-
continue;
43-
}
44-
[entryData writeToFile:writeToPath atomically:YES];
45-
} else {
46-
callback(NO, zipError);
47-
return;
48-
}
49-
}
50-
// done:
51-
if (self.plugin.openPreferencesOnInstall) {
52-
dispatch_async(dispatch_get_main_queue(), ^{
53-
AppDelegate *d = (id)[NSApp delegate];
54-
[self.plugin presentOptionsInWindow:d.window];
55-
});
56-
}
57-
[[UpdateChecker shared] justInstalledPlugin:self.plugin.name];
58-
callback(YES, nil);
59-
} else {
60-
callback(NO, zipError);
61-
}
62-
});
29+
[self installPluginData:data intoPluginsDirectory:directory callback:callback];
6330
} else {
6431
callback(NO, error);
6532
}
6633
}] resume];
6734
}
6835
}
6936

37+
- (NSString *)nameForPluginContainingPath:(NSString *)path {
38+
for (NSString *comp in path.pathComponents.reverseObjectEnumerator) {
39+
if ([comp.pathExtension isEqualToString:@"bundle"]) {
40+
return comp.stringByDeletingPathExtension;
41+
}
42+
}
43+
return nil;
44+
}
45+
46+
- (void)installPluginData:(NSData *)data intoPluginsDirectory:(NSString *)directory callback:(void(^)(BOOL success, NSError *error))callback {
47+
if (!data) {
48+
callback(NO, nil);
49+
return;
50+
}
51+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
52+
NSString *pluginName = nil;
53+
NSError *zipError = nil;
54+
ZZArchive *archive = [ZZArchive archiveWithData:data error:&zipError];
55+
if (archive && !zipError) {
56+
for (ZZArchiveEntry *entry in archive.entries) {
57+
zipError = nil;
58+
NSData *entryData = [entry newDataWithError:&zipError];
59+
if (entryData && !zipError) {
60+
NSString *writeToPath = [directory stringByAppendingPathComponent:entry.fileName];
61+
if (![[NSFileManager defaultManager] fileExistsAtPath:[writeToPath stringByDeletingLastPathComponent]]) {
62+
[[NSFileManager defaultManager] createDirectoryAtPath:[writeToPath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:NO];
63+
}
64+
if ([[writeToPath pathExtension] isEqualToString:@"bundle"]) {
65+
continue;
66+
}
67+
if (!pluginName && [self nameForPluginContainingPath:writeToPath]) {
68+
pluginName = [self nameForPluginContainingPath:writeToPath];
69+
}
70+
[entryData writeToFile:writeToPath atomically:YES];
71+
} else {
72+
callback(NO, zipError);
73+
return;
74+
}
75+
}
76+
if (!pluginName) {
77+
callback(NO, nil);
78+
return;
79+
}
80+
_installedPluginName = pluginName;
81+
PluginModel *pluginModel = [PluginModel installedPluginNamed:pluginName];
82+
// done:
83+
if (pluginModel.openPreferencesOnInstall) {
84+
dispatch_async(dispatch_get_main_queue(), ^{
85+
AppDelegate *d = (id)[NSApp delegate];
86+
[pluginModel presentOptionsInWindow:d.window];
87+
});
88+
}
89+
[[UpdateChecker shared] justInstalledPlugin:self.plugin.name];
90+
callback(YES, nil);
91+
} else {
92+
callback(NO, zipError);
93+
}
94+
});
95+
}
96+
7097
@end

FlashlightApp/EasySIMBL/PluginListController.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
@property (nonatomic) IBOutlet NSOutlineView *sourceList;
3838

3939
- (void)showPluginWithName:(NSString *)name;
40+
- (void)showInstalledPluginWithName:(NSString *)name;
4041

4142
@property (nonatomic) BOOL enabled;
4243

FlashlightApp/EasySIMBL/PluginListController.m

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,4 +601,16 @@ - (void)showPluginWithName:(NSString *)name {
601601
self.selectedCategory = kCategoryShowIndividualPlugin;
602602
}
603603

604+
- (void)showInstalledPluginWithName:(NSString *)name {
605+
self.selectedCategory = @"Installed";
606+
self.selectedPluginName = name;
607+
PluginModel *model = [self.installedPlugins map:^id(id obj) {
608+
return [[obj name] isEqualToString:name] ? obj : nil;
609+
}].firstObject;
610+
NSUInteger index = model ? [self.installedPlugins indexOfObject:model] : NSNotFound;
611+
if (index != NSNotFound) {
612+
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];
613+
}
614+
}
615+
604616
@end

FlashlightApp/Flashlight.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
5FD3EA841A204CA1005A312E /* NSURLComponents+ValueForQueryKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FD3EA831A204CA1005A312E /* NSURLComponents+ValueForQueryKey.m */; };
5252
5FF5D4111A675F0800641D7A /* UpdateChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FF5D4101A675F0800641D7A /* UpdateChecker.m */; };
5353
5FF873161A6D71EA00EAD72F /* NSObject+InternationalizedValueForKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FF873151A6D71EA00EAD72F /* NSObject+InternationalizedValueForKey.m */; };
54+
5FF8731D1A6D977500EAD72F /* GenericPluginIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 5FF8731C1A6D977500EAD72F /* GenericPluginIcon.icns */; };
5455
6C3A4C44159EADF900985CCD /* FlashlightSIMBLAgent.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 6C3A4C35159E7E7000985CCD /* FlashlightSIMBLAgent.app */; };
5556
6C564485159E7C0800215467 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C564484159E7C0800215467 /* Cocoa.framework */; };
5657
6C56448F159E7C0800215467 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6C56448D159E7C0800215467 /* InfoPlist.strings */; };
@@ -204,6 +205,7 @@
204205
5FF5D4101A675F0800641D7A /* UpdateChecker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UpdateChecker.m; sourceTree = "<group>"; };
205206
5FF873141A6D71EA00EAD72F /* NSObject+InternationalizedValueForKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+InternationalizedValueForKey.h"; path = "../FlashlightKit/FlashlightKit/NSObject+InternationalizedValueForKey.h"; sourceTree = "<group>"; };
206207
5FF873151A6D71EA00EAD72F /* NSObject+InternationalizedValueForKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+InternationalizedValueForKey.m"; path = "../FlashlightKit/FlashlightKit/NSObject+InternationalizedValueForKey.m"; sourceTree = "<group>"; };
208+
5FF8731C1A6D977500EAD72F /* GenericPluginIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = GenericPluginIcon.icns; sourceTree = "<group>"; };
207209
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>"; };
208210
658988FB1A270ACF00ABE5E9 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
209211
6C3A4C2B159E7E6F00985CCD /* SIMBL.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = SIMBL.xcodeproj; sourceTree = "<group>"; };
@@ -461,6 +463,7 @@
461463
6C56448B159E7C0800215467 /* Supporting Files */ = {
462464
isa = PBXGroup;
463465
children = (
466+
5FF8731C1A6D977500EAD72F /* GenericPluginIcon.icns */,
464467
5FC0166E1A1DBE7300F8E4E7 /* Localizable.strings */,
465468
5F6326C31A083E1F00CE241E /* Icon.icns */,
466469
6C56448C159E7C0800215467 /* Flashlight-Info.plist */,
@@ -562,6 +565,7 @@
562565
files = (
563566
5FBEC9C41A166C00007FEC54 /* dsa_pub.pem in Resources */,
564567
6C56448F159E7C0800215467 /* InfoPlist.strings in Resources */,
568+
5FF8731D1A6D977500EAD72F /* GenericPluginIcon.icns in Resources */,
565569
5F60B0FE1A196562004BCF08 /* WorkflowTemplate.bundle in Resources */,
566570
5F2B37761A3E42E500AC2C19 /* SearchPluginEditorWindowController.xib in Resources */,
567571
5F6326C41A083E1F00CE241E /* Icon.icns in Resources */,

0 commit comments

Comments
 (0)