diff --git a/Configuration/Common.xcconfig b/Configuration/Common.xcconfig new file mode 100644 index 0000000..4d9ff49 --- /dev/null +++ b/Configuration/Common.xcconfig @@ -0,0 +1,10 @@ +ARCHS = $(ARCHS_STANDARD) +MACOSX_DEPLOYMENT_TARGET = 11.0 +FRAMEWORK_SEARCH_PATHS = /Applications/Quicksilver.app/Contents/Frameworks +INFOPLIST_FILE = Info.plist +PRODUCT_NAME = Social Bookmarks Plugin +WRAPPER_EXTENSION = qsplugin +ALWAYS_SEARCH_USER_PATHS = NO +CLANG_ENABLE_OBJC_ARC = NO +GCC_PREFIX_HEADER = Configuration/Quicksilver.pch +GCC_PRECOMPILE_PREFIX_HEADER = YES diff --git a/Configuration/Debug.xcconfig b/Configuration/Debug.xcconfig new file mode 100644 index 0000000..2dbe2d9 --- /dev/null +++ b/Configuration/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Common.xcconfig" +GCC_OPTIMIZATION_LEVEL = 0 diff --git a/Configuration/QSPlugIn.xcconfig b/Configuration/QSPlugIn.xcconfig new file mode 100644 index 0000000..96d93dc --- /dev/null +++ b/Configuration/QSPlugIn.xcconfig @@ -0,0 +1 @@ +#include "Common.xcconfig" diff --git a/Configuration/Quicksilver.pch b/Configuration/Quicksilver.pch new file mode 100644 index 0000000..4d58295 --- /dev/null +++ b/Configuration/Quicksilver.pch @@ -0,0 +1,5 @@ +#ifdef __OBJC__ +#import +#import +#import +#endif diff --git a/Configuration/Release.xcconfig b/Configuration/Release.xcconfig new file mode 100644 index 0000000..97c5827 --- /dev/null +++ b/Configuration/Release.xcconfig @@ -0,0 +1,3 @@ +#include "Common.xcconfig" +GCC_OPTIMIZATION_LEVEL = s +COPY_PHASE_STRIP = YES diff --git a/QSDeliciousPlugIn.xcodeproj/project.pbxproj b/QSDeliciousPlugIn.xcodeproj/project.pbxproj index ce2a3d5..a7b150c 100644 --- a/QSDeliciousPlugIn.xcodeproj/project.pbxproj +++ b/QSDeliciousPlugIn.xcodeproj/project.pbxproj @@ -44,7 +44,7 @@ E182BCD006FC8203007BF2C2 /* QSDeliciousPlugIn_Source.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = QSDeliciousPlugIn_Source.m; sourceTree = ""; }; E182BE2206FC9AB5007BF2C2 /* QSDeliciousPrefPane.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = QSDeliciousPrefPane.h; sourceTree = ""; }; E182BE2306FC9AB5007BF2C2 /* QSDeliciousPrefPane.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = QSDeliciousPrefPane.m; sourceTree = ""; }; - E182BE3B06FC9B13007BF2C2 /* en */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + E182BE3B06FC9B13007BF2C2 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; E182BE5A06FC9C46007BF2C2 /* PreferencePanes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PreferencePanes.framework; path = /System/Library/Frameworks/PreferencePanes.framework; sourceTree = ""; }; /* End PBXFileReference section */ @@ -139,8 +139,8 @@ D475F9B418B2992D0012243C /* Release.xcconfig */, ); name = Configuration; - path = /private/tmp/QS/Configuration; - sourceTree = ""; + path = Configuration; + sourceTree = ""; }; /* End PBXGroup section */ @@ -254,6 +254,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D475F9B218B2992D0012243C /* QSPlugIn.xcconfig */; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; ZERO_LINK = YES; }; name = Debug; @@ -262,6 +263,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D475F9B218B2992D0012243C /* QSPlugIn.xcconfig */; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; ZERO_LINK = NO; }; name = Release; @@ -270,6 +272,12 @@ isa = XCBuildConfiguration; baseConfigurationReference = D475F9B018B2992D0012243C /* Debug.xcconfig */; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; + FRAMEWORK_SEARCH_PATHS = /Applications/Quicksilver.app/Contents/Frameworks; + INFOPLIST_FILE = Info.plist; + PRODUCT_NAME = "Social Bookmarks Plugin"; + WRAPPER_EXTENSION = qsplugin; + MACOSX_DEPLOYMENT_TARGET = 11.0; }; name = Debug; }; @@ -277,6 +285,12 @@ isa = XCBuildConfiguration; baseConfigurationReference = D475F9B418B2992D0012243C /* Release.xcconfig */; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; + FRAMEWORK_SEARCH_PATHS = /Applications/Quicksilver.app/Contents/Frameworks; + INFOPLIST_FILE = Info.plist; + PRODUCT_NAME = "Social Bookmarks Plugin"; + WRAPPER_EXTENSION = qsplugin; + MACOSX_DEPLOYMENT_TARGET = 11.0; }; name = Release; }; diff --git a/QSDeliciousPlugIn_Source.h b/QSDeliciousPlugIn_Source.h index 89729de..29d5bd2 100644 --- a/QSDeliciousPlugIn_Source.h +++ b/QSDeliciousPlugIn_Source.h @@ -7,7 +7,9 @@ // -#import "QSDeliciousPlugIn_Source.h" +#import +#import +#import @interface QSDeliciousPlugIn_Source : QSObjectSource { NSMutableArray *posts; diff --git a/QSDeliciousPlugIn_Source.m b/QSDeliciousPlugIn_Source.m index 8dcc9b4..afccbb2 100644 --- a/QSDeliciousPlugIn_Source.m +++ b/QSDeliciousPlugIn_Source.m @@ -32,7 +32,57 @@ - (NSString *) mainNibName { } - (void)populateFields { - NSLog(@"populating: %@/%@", [self.selectedEntry.sourceSettings objectForKey:@"username"], [self.selectedEntry.sourceSettings objectForKey:@"site"]); + NSMutableDictionary *settings = self.selectedEntry.sourceSettings; + NSLog(@"QSDelicious: populateFields, settings=%@", settings); + + NSString *username = [settings objectForKey:@"username"]; + if (username) + [userField setStringValue:username]; + else + [userField setStringValue:@""]; + [userField unbind:@"value"]; + [userField setEditable:YES]; + [userField setSelectable:YES]; + [userField setTarget:self]; + [userField setAction:@selector(saveUsername:)]; + + NSView *container = [self settingsView]; + for (NSView *subview in [container subviews]) { + if ([subview isKindOfClass:[NSPopUpButton class]]) { + NSPopUpButton *popup = (NSPopUpButton *)subview; + [popup unbind:@"selectedTag"]; + NSInteger siteIdx = [settings objectForKey:@"site"] != nil ? [[settings objectForKey:@"site"] integerValue] : 0; + [popup selectItemAtIndex:siteIdx]; + [popup setTarget:self]; + [popup setAction:@selector(saveSite:)]; + } else if ([subview isKindOfClass:[NSButton class]] && [[subview cell] isKindOfClass:[NSButtonCell class]]) { + NSButton *button = (NSButton *)subview; + if ([[button title] containsString:@"tag"] || [[button title] containsString:@"Tag"]) { + [button unbind:@"value"]; + BOOL includeTags = [[settings objectForKey:@"includeTags"] boolValue]; + [button setState:includeTags ? NSControlStateValueOn : NSControlStateValueOff]; + [button setTarget:self]; + [button setAction:@selector(saveIncludeTags:)]; + } + } + } +} + +- (void)saveUsername:(id)sender { + [self.selectedEntry.sourceSettings setObject:[userField stringValue] forKey:@"username"]; + [[NSNotificationCenter defaultCenter] postNotificationName:QSCatalogEntryChangedNotification object:self.selectedEntry]; +} + +- (void)saveSite:(id)sender { + NSPopUpButton *popup = (NSPopUpButton *)sender; + [self.selectedEntry.sourceSettings setObject:[NSNumber numberWithInteger:[popup indexOfSelectedItem]] forKey:@"site"]; + [[NSNotificationCenter defaultCenter] postNotificationName:QSCatalogEntryChangedNotification object:self.selectedEntry]; +} + +- (void)saveIncludeTags:(id)sender { + NSButton *button = (NSButton *)sender; + [self.selectedEntry.sourceSettings setObject:[NSNumber numberWithBool:([button state] == NSControlStateValueOn)] forKey:@"includeTags"]; + [[NSNotificationCenter defaultCenter] postNotificationName:QSCatalogEntryChangedNotification object:self.selectedEntry]; } - (NSView *) settingsView { @@ -220,24 +270,46 @@ - (NSData *)cachedBookmarkData { } - (NSData *)bookmarkData { - if (![self currentUsername] || ![self currentPassword]) return nil; - - NSString *urlString = [NSString stringWithFormat:@"https://%@:%@@%@/posts/all?", [self currentUsername], [self currentPassword], [self currentAPIURL]]; + NSLog(@"QSDelicious: siteIndex=%ld apiURL=%@ username=%@", (long)[self siteIndex], [self currentAPIURL], [self currentUsername]); + if (![self currentUsername] || ![self currentPassword]) { + NSLog(@"QSDelicious: missing username or password"); + return nil; + } + + NSString *urlString = [NSString stringWithFormat:@"https://%@/posts/all", [self currentAPIURL]]; NSURL *requestURL = [NSURL URLWithString:urlString]; - + NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:requestURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; - [theRequest setHTTPMethod:@"POST"]; - [theRequest setValue:@"text/xml" forHTTPHeaderField:@"Content-type"]; - [theRequest setValue:@"Quicksilver (Blacktree,MacOSX)" forHTTPHeaderField:@"User-Agent"]; - - NSError *error; - NSData *data = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:nil error:&error]; - + [theRequest setHTTPMethod:@"GET"]; + + NSString *credentials = [NSString stringWithFormat:@"%@:%@", [self currentUsername], [self currentPassword]]; + NSData *credData = [credentials dataUsingEncoding:NSUTF8StringEncoding]; + NSString *base64 = [credData base64EncodedStringWithOptions:0]; + [theRequest setValue:[NSString stringWithFormat:@"Basic %@", base64] forHTTPHeaderField:@"Authorization"]; + [theRequest setValue:@"Quicksilver (Blacktree,MacOSX)" forHTTPHeaderField:@"User-Agent"]; + + NSURLResponse *response = nil; + NSError *error = nil; + NSData *data = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&response error:&error]; + + if (error) { + NSLog(@"QSDelicious: fetch error: %@", error); + return nil; + } + + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + NSLog(@"QSDelicious: HTTP %ld, received %lu bytes", (long)[httpResponse statusCode], (unsigned long)[data length]); + + if ([httpResponse statusCode] != 200) { + NSLog(@"QSDelicious: response body: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); + return nil; + } + NSString *cachePath = [QSApplicationSupportSubPath([NSString stringWithFormat:@"Caches/%@/", [self currentSiteURL]], YES) stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.xml", [self currentUsername]]]; - [data writeToFile:cachePath atomically:NO]; - + [data writeToFile:cachePath atomically:NO]; + return data; }