summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Factories/QSBookmarkProviderFactory.h10
-rw-r--r--Factories/QSBookmarkProviderFactory.m70
-rw-r--r--Protocols/QSBookmarkProvider.h20
-rw-r--r--Providers/QSDeliciousAPIProvider.h21
-rw-r--r--Providers/QSDeliciousAPIProvider.m360
-rw-r--r--Providers/QSLinkdingProvider.h9
-rw-r--r--Providers/QSLinkdingProvider.m348
-rw-r--r--QSDeliciousPlugIn_Source.h8
-rw-r--r--QSDeliciousPlugIn_Source.m349
-rw-r--r--Types/SocialSite.h10
-rw-r--r--Types/SocialSite.m70
11 files changed, 732 insertions, 543 deletions
diff --git a/Factories/QSBookmarkProviderFactory.h b/Factories/QSBookmarkProviderFactory.h
index e2e694b..bc0ae79 100644
--- a/Factories/QSBookmarkProviderFactory.h
+++ b/Factories/QSBookmarkProviderFactory.h
@@ -5,20 +5,24 @@
// Factory for managing bookmark providers
//
-#import <Foundation/Foundation.h>
#import "QSBookmarkProvider.h"
#import "SocialSite.h"
+#import <Foundation/Foundation.h>
@interface QSBookmarkProviderFactory : NSObject
-@property (nonatomic, strong, readonly) NSArray<id<QSBookmarkProvider>> *providers;
+@property(nonatomic, strong, readonly)
+ NSArray<id<QSBookmarkProvider>> *providers;
+ (instancetype)sharedFactory;
/**
* Get the appropriate provider for the given site configuration
*/
-- (id<QSBookmarkProvider>)providerForSite:(SocialSite)site username:(NSString *)username password:(NSString *)password host:(NSString *)host;
+- (id<QSBookmarkProvider>)providerForSite:(SocialSite)site
+ username:(NSString *)username
+ password:(NSString *)password
+ host:(NSString *)host;
/**
* Get all available providers
diff --git a/Factories/QSBookmarkProviderFactory.m b/Factories/QSBookmarkProviderFactory.m
index 75fd0e2..56b4146 100644
--- a/Factories/QSBookmarkProviderFactory.m
+++ b/Factories/QSBookmarkProviderFactory.m
@@ -9,54 +9,62 @@
#import "SocialSite.h"
@interface QSBookmarkProviderFactory ()
-@property (nonatomic, strong, readwrite) NSArray<id<QSBookmarkProvider>> *providers;
+@property(nonatomic, strong, readwrite)
+ NSArray<id<QSBookmarkProvider>> *providers;
@end
@implementation QSBookmarkProviderFactory
+ (instancetype)sharedFactory {
- static QSBookmarkProviderFactory *sharedInstance = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- sharedInstance = [[self alloc] init];
- });
- return sharedInstance;
+ static QSBookmarkProviderFactory *sharedInstance = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ sharedInstance = [[self alloc] init];
+ });
+ return sharedInstance;
}
- (instancetype)init {
- self = [super init];
- if (self) {
- [self setupProviders];
- }
- return self;
+ self = [super init];
+ if (self) {
+ [self setupProviders];
+ }
+ return self;
}
- (void)setupProviders {
- NSMutableArray *mutableProviders = [NSMutableArray array];
-
- // Create Delicious / Pinboard API providers for each supported site
- QSDeliciousAPIProvider *deliciousAPIProvider = [[QSDeliciousAPIProvider alloc] init];
-
- // Create Linkding provider
- QSLinkdingProvider *linkdingProvider = [[QSLinkdingProvider alloc] init];
-
- [mutableProviders addObject:deliciousAPIProvider];
- [mutableProviders addObject:linkdingProvider];
-
- self.providers = [mutableProviders copy];
+ NSMutableArray *mutableProviders = [NSMutableArray array];
+
+ // Create Delicious / Pinboard API providers for each supported site
+ QSDeliciousAPIProvider *deliciousAPIProvider =
+ [[QSDeliciousAPIProvider alloc] init];
+
+ // Create Linkding provider
+ QSLinkdingProvider *linkdingProvider = [[QSLinkdingProvider alloc] init];
+
+ [mutableProviders addObject:deliciousAPIProvider];
+ [mutableProviders addObject:linkdingProvider];
+
+ self.providers = [mutableProviders copy];
}
-- (id<QSBookmarkProvider>)providerForSite:(SocialSite)site username:(NSString *)username password:(NSString *)password host:(NSString *)host {
- for (id<QSBookmarkProvider> provider in self.providers) {
- if ([provider canHandleSite:site username:username password:password host:host]) {
- return provider;
- }
+- (id<QSBookmarkProvider>)providerForSite:(SocialSite)site
+ username:(NSString *)username
+ password:(NSString *)password
+ host:(NSString *)host {
+ for (id<QSBookmarkProvider> provider in self.providers) {
+ if ([provider canHandleSite:site
+ username:username
+ password:password
+ host:host]) {
+ return provider;
}
- return nil;
+ }
+ return nil;
}
- (NSArray<id<QSBookmarkProvider>> *)allProviders {
- return self.providers;
+ return self.providers;
}
@end
diff --git a/Protocols/QSBookmarkProvider.h b/Protocols/QSBookmarkProvider.h
index 9131e3c..48e1417 100644
--- a/Protocols/QSBookmarkProvider.h
+++ b/Protocols/QSBookmarkProvider.h
@@ -5,8 +5,8 @@
// Protocol for social bookmark providers
//
-#import <Foundation/Foundation.h>
#import "SocialSite.h"
+#import <Foundation/Foundation.h>
@class QSObject;
@@ -16,18 +16,30 @@
/**
* Check if this provider can handle the given site configuration
*/
-- (BOOL)canHandleSite:(SocialSite)site username:(NSString *)username password:(NSString *)password host:(NSString *)host;
+- (BOOL)canHandleSite:(SocialSite)site
+ username:(NSString *)username
+ password:(NSString *)password
+ host:(NSString *)host;
/**
* Fetch bookmarks for the given configuration
* Returns an NSArray of QSObject instances
*/
-- (NSArray *)fetchBookmarksForSite:(SocialSite)site username:(NSString *)username password:(NSString *)password identifier:(NSString *)identifier host:(NSString *)host includeTags:(BOOL)includeTags;
+- (NSArray *)fetchBookmarksForSite:(SocialSite)site
+ username:(NSString *)username
+ password:(NSString *)password
+ identifier:(NSString *)identifier
+ host:(NSString *)host
+ includeTags:(BOOL)includeTags;
@optional
/**
* Get bookmarks for a specific tag (used for child loading)
*/
-- (NSArray *)fetchBookmarksForTag:(NSString *)tag site:(SocialSite)site username:(NSString *)username password:(NSString *)password host:(NSString *)host;
+- (NSArray *)fetchBookmarksForTag:(NSString *)tag
+ site:(SocialSite)site
+ username:(NSString *)username
+ password:(NSString *)password
+ host:(NSString *)host;
@end
diff --git a/Providers/QSDeliciousAPIProvider.h b/Providers/QSDeliciousAPIProvider.h
index 7979812..310b00b 100644
--- a/Providers/QSDeliciousAPIProvider.h
+++ b/Providers/QSDeliciousAPIProvider.h
@@ -6,17 +6,26 @@
// Used by Delicious, Magnolia, and Pinboard
//
-#import <Foundation/Foundation.h>
#import "QSBookmarkProvider.h"
+#import <Foundation/Foundation.h>
-@interface QSDeliciousAPIProvider : NSObject <QSBookmarkProvider, NSXMLParserDelegate>
+@interface QSDeliciousAPIProvider
+ : NSObject <QSBookmarkProvider, NSXMLParserDelegate>
-@property (nonatomic, strong) NSMutableArray *posts;
+@property(nonatomic, strong) NSMutableArray *posts;
// Subclasses can override these methods
- (NSString *)apiURLForSite:(SocialSite)site andHost:(NSString *)host;
-- (NSURL *)requestURLForSite:(SocialSite)site username:(NSString *)username password:(NSString *)password host:(NSString *)host;
-- (NSData *)cachedBookmarkDataForSite:(SocialSite)site username:(NSString *)username host:(NSString *)host;
-- (void)cacheBookmarkData:(NSData *)data forSite:(SocialSite)site username:(NSString *)username host:(NSString *)host;
+- (NSURL *)requestURLForSite:(SocialSite)site
+ username:(NSString *)username
+ password:(NSString *)password
+ host:(NSString *)host;
+- (NSData *)cachedBookmarkDataForSite:(SocialSite)site
+ username:(NSString *)username
+ host:(NSString *)host;
+- (void)cacheBookmarkData:(NSData *)data
+ forSite:(SocialSite)site
+ username:(NSString *)username
+ host:(NSString *)host;
@end
diff --git a/Providers/QSDeliciousAPIProvider.m b/Providers/QSDeliciousAPIProvider.m
index 0c37a43..03991c1 100644
--- a/Providers/QSDeliciousAPIProvider.m
+++ b/Providers/QSDeliciousAPIProvider.m
@@ -4,202 +4,264 @@
//
#import "QSDeliciousAPIProvider.h"
-#import "SocialSite.h"
#import "Constants.h"
+#import "SocialSite.h"
#import <QSCore/QSCore.h>
@implementation QSDeliciousAPIProvider
-- (BOOL)canHandleSite:(SocialSite)site username:(NSString *)username password:(NSString *)password host:(NSString *)host {
- return (
- site == SocialSiteDelicious ||
- site == SocialSiteMagnolia ||
- site == SocialSitePinboard ||
- site == SocialSiteSelfHostedDeliciousCompatible
- ) &&
- username.length > 0 &&
- password.length > 0 &&
- (site != SocialSiteSelfHostedDeliciousCompatible || host.length > 0);
+- (BOOL)canHandleSite:(SocialSite)site
+ username:(NSString *)username
+ password:(NSString *)password
+ host:(NSString *)host {
+ return (site == SocialSiteDelicious || site == SocialSiteMagnolia ||
+ site == SocialSitePinboard ||
+ site == SocialSiteSelfHostedDeliciousCompatible) &&
+ username.length > 0 && password.length > 0 &&
+ (site != SocialSiteSelfHostedDeliciousCompatible || host.length > 0);
}
- (NSString *)apiURLForSite:(SocialSite)site andHost:(NSString *)host {
- switch (site) {
- case SocialSiteDelicious:
- return @"api.del.icio.us/v1";
- case SocialSiteMagnolia:
- return @"ma.gnolia.com/api/mirrord/v1";
- case SocialSitePinboard:
- return @"https://api.pinboard.in/v1";
- case SocialSiteSelfHostedDeliciousCompatible:
- return [NSString stringWithFormat:@"%@/v1", host];
- default:
- return nil;
- }
+ switch (site) {
+ case SocialSiteDelicious:
+ return @"api.del.icio.us/v1";
+ case SocialSiteMagnolia:
+ return @"ma.gnolia.com/api/mirrord/v1";
+ case SocialSitePinboard:
+ return @"https://api.pinboard.in/v1";
+ case SocialSiteSelfHostedDeliciousCompatible:
+ return [NSString stringWithFormat:@"%@/v1", host];
+ default:
+ return nil;
+ }
}
- (BOOL)usesAuthToken:(SocialSite)site {
- switch (site) {
- case SocialSitePinboard:
- case SocialSiteSelfHostedDeliciousCompatible:
- return YES;
- default:
- return NO;
- }
+ switch (site) {
+ case SocialSitePinboard:
+ case SocialSiteSelfHostedDeliciousCompatible:
+ return YES;
+ default:
+ return NO;
+ }
}
-- (NSURL *)requestURLForSite:(SocialSite)site username:(NSString *)username password:(NSString *)password host:(NSString *)host {
- NSString *apiURL = [self apiURLForSite:site andHost: host];
- if (!apiURL) return nil;
-
+- (NSURL *)requestURLForSite:(SocialSite)site
+ username:(NSString *)username
+ password:(NSString *)password
+ host:(NSString *)host {
+ NSString *apiURL = [self apiURLForSite:site andHost:host];
+ if (!apiURL)
+ return nil;
+
NSString *urlString;
if ([self usesAuthToken:site]) {
// Pinboard and pinboard compatible sites require an
// auth token rather than a password.
- urlString = [NSString stringWithFormat:@"%@/posts/all?auth_token=%@", apiURL, password];
+ urlString = [NSString
+ stringWithFormat:@"%@/posts/all?auth_token=%@", apiURL, password];
} else {
- urlString = [NSString stringWithFormat:@"https://%@:%@@%@/posts/all?", username, password, apiURL];
+ urlString = [NSString stringWithFormat:@"https://%@:%@@%@/posts/all?",
+ username, password, apiURL];
}
- return [NSURL URLWithString:urlString];
+ return [NSURL URLWithString:urlString];
}
-# pragma mark - Cache
+#pragma mark - Cache
+
+- (NSString *)cachePathForSite:(SocialSite)site
+ username:(NSString *)username
+ host:(NSString *)host
+ create:(BOOL)create {
-- (NSString *)cachePathForSite:(SocialSite)site username:(NSString *)username host:(NSString *)host create:(BOOL) create {
-
NSString *siteURL = [SocialSiteHelper cacheKeyForSite:site];
- NSString *safeHost = [[host componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@"-"];
- return [QSApplicationSupportSubPath([NSString stringWithFormat:@"Caches/%@/", siteURL], create) stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-%@.xml", safeHost, username]];
+ NSString *safeHost =
+ [[host componentsSeparatedByCharactersInSet:[[NSCharacterSet
+ alphanumericCharacterSet]
+ invertedSet]]
+ componentsJoinedByString:@"-"];
+ return [QSApplicationSupportSubPath(
+ [NSString stringWithFormat:@"Caches/%@/", siteURL], create)
+ stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-%@.xml",
+ safeHost,
+ username]];
}
-
-- (NSData *)cachedBookmarkDataForSite:(SocialSite)site username:(NSString *)username host:(NSString *)host {
- NSString *cachePath = [self cachePathForSite: site username: username host: host create: NO];
+- (NSData *)cachedBookmarkDataForSite:(SocialSite)site
+ username:(NSString *)username
+ host:(NSString *)host {
+ NSString *cachePath = [self cachePathForSite:site
+ username:username
+ host:host
+ create:NO];
return [NSData dataWithContentsOfFile:cachePath];
}
-- (void)cacheBookmarkData:(NSData *)data forSite:(SocialSite)site username:(NSString *)username host:(NSString *)host {
- NSString *cachePath = [self cachePathForSite: site username: username host: host create: YES];
- [data writeToFile:cachePath atomically:NO];
+- (void)cacheBookmarkData:(NSData *)data
+ forSite:(SocialSite)site
+ username:(NSString *)username
+ host:(NSString *)host {
+ NSString *cachePath = [self cachePathForSite:site
+ username:username
+ host:host
+ create:YES];
+ [data writeToFile:cachePath atomically:NO];
}
-- (NSArray *)fetchBookmarksForSite:(SocialSite)site username:(NSString *)username password:(NSString *)password identifier:(NSString *)identifier host:(NSString *)host includeTags:(BOOL)includeTags {
-
- // Try cached data first
- NSData *data = [self cachedBookmarkDataForSite:site username:username host:host];
+- (NSArray *)fetchBookmarksForSite:(SocialSite)site
+ username:(NSString *)username
+ password:(NSString *)password
+ identifier:(NSString *)identifier
+ host:(NSString *)host
+ includeTags:(BOOL)includeTags {
- // If no cached data, fetch from API
- if (![data length]) {
- NSURL *requestURL = [self requestURLForSite:site username:username password:password host:host];
- if (!requestURL) return @[];
-
- NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:requestURL
- cachePolicy:NSURLRequestUseProtocolCachePolicy
- timeoutInterval:60.0];
- [theRequest setHTTPMethod:@"GET"];
- [theRequest setValue:@"text/xml" forHTTPHeaderField:@"Content-type"];
- [theRequest setValue:@"Quicksilver (Blacktree,MacOSX)" forHTTPHeaderField:@"User-Agent"];
-
- NSError *error = nil;
- data = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:nil error:&error];
-
- if (error) {
- NSLog(@"Error fetching bookmarks: %@", error.localizedDescription);
- return @[];
- }
-
- // Cache the data
- [self cacheBookmarkData:data forSite:site username:username host:host];
+ // Try cached data first
+ NSData *data = [self cachedBookmarkDataForSite:site
+ username:username
+ host:host];
+
+ // If no cached data, fetch from API
+ if (![data length]) {
+ NSURL *requestURL = [self requestURLForSite:site
+ username:username
+ password:password
+ host:host];
+ if (!requestURL)
+ return @[];
+
+ NSMutableURLRequest *theRequest =
+ [NSMutableURLRequest requestWithURL:requestURL
+ cachePolicy:NSURLRequestUseProtocolCachePolicy
+ timeoutInterval:60.0];
+ [theRequest setHTTPMethod:@"GET"];
+ [theRequest setValue:@"text/xml" forHTTPHeaderField:@"Content-type"];
+ [theRequest setValue:@"Quicksilver (Blacktree,MacOSX)"
+ forHTTPHeaderField:@"User-Agent"];
+
+ NSError *error = nil;
+ data = [NSURLConnection sendSynchronousRequest:theRequest
+ returningResponse:nil
+ error:&error];
+
+ if (error) {
+ NSLog(@"Error fetching bookmarks: %@", error.localizedDescription);
+ return @[];
}
-
- // Parse XML data
- NSXMLParser *postParser = [[NSXMLParser alloc] initWithData:data];
- [postParser setDelegate:self];
-
- self.posts = [NSMutableArray arrayWithCapacity:1];
- [postParser parse];
-
- NSMutableArray *objects = [NSMutableArray arrayWithCapacity:1];
- NSMutableSet *tagSet = [NSMutableSet set];
-
- // Create bookmark objects
- for (NSDictionary *post in self.posts) {
- QSObject *newObject = [self objectForPost:post];
- if (newObject) {
- [objects addObject:newObject];
-
- // Collect tags if requested
- if (includeTags) {
- NSString *tagString = [post objectForKey:@"tag"];
- if (tagString.length > 0) {
- [tagSet addObjectsFromArray:[tagString componentsSeparatedByString:@" "]];
- }
- }
+
+ // Cache the data
+ [self cacheBookmarkData:data forSite:site username:username host:host];
+ }
+
+ // Parse XML data
+ NSXMLParser *postParser = [[NSXMLParser alloc] initWithData:data];
+ [postParser setDelegate:self];
+
+ self.posts = [NSMutableArray arrayWithCapacity:1];
+ [postParser parse];
+
+ NSMutableArray *objects = [NSMutableArray arrayWithCapacity:1];
+ NSMutableSet *tagSet = [NSMutableSet set];
+
+ // Create bookmark objects
+ for (NSDictionary *post in self.posts) {
+ QSObject *newObject = [self objectForPost:post];
+ if (newObject) {
+ [objects addObject:newObject];
+
+ // Collect tags if requested
+ if (includeTags) {
+ NSString *tagString = [post objectForKey:@"tag"];
+ if (tagString.length > 0) {
+ [tagSet
+ addObjectsFromArray:[tagString componentsSeparatedByString:@" "]];
}
+ }
}
-
- // Create tag objects if requested
- if (includeTags) {
- for (NSString *tag in tagSet) {
- if (tag.length > 0) {
- QSObject *tagObject = [QSObject makeObjectWithIdentifier:[NSString stringWithFormat:@"[%@ tag]:%@", [SocialSiteHelper displayNameForSite:site], tag]];
+ }
- [tagObject setObject:tag forType:kTagType];
- [tagObject setObject:@(site) forMeta:kTagSiteField];
- [tagObject setObject:username forMeta:kTagUsernameField];
- [tagObject setObject:host forMeta:kTagHostField];
- [tagObject setObject:identifier forMeta:kTagIdentifierField];
- [tagObject setName:tag];
- [tagObject setPrimaryType:kTagType];
- [objects addObject:tagObject];
- }
- }
+ // Create tag objects if requested
+ if (includeTags) {
+ for (NSString *tag in tagSet) {
+ if (tag.length > 0) {
+ QSObject *tagObject = [QSObject
+ makeObjectWithIdentifier:
+ [NSString
+ stringWithFormat:@"[%@ tag]:%@",
+ [SocialSiteHelper displayNameForSite:site],
+ tag]];
+
+ [tagObject setObject:tag forType:kTagType];
+ [tagObject setObject:@(site) forMeta:kTagSiteField];
+ [tagObject setObject:username forMeta:kTagUsernameField];
+ [tagObject setObject:host forMeta:kTagHostField];
+ [tagObject setObject:identifier forMeta:kTagIdentifierField];
+ [tagObject setName:tag];
+ [tagObject setPrimaryType:kTagType];
+ [objects addObject:tagObject];
+ }
}
-
- return objects;
+ }
+
+ return objects;
}
-- (NSArray *)fetchBookmarksForTag:(NSString *)tag site:(SocialSite)site username:(NSString *)username password:(NSString *)password host:(NSString *)host {
- NSData *data = [self cachedBookmarkDataForSite:site username:username host:host];
- if (!data) return @[];
-
- NSXMLParser *postParser = [[NSXMLParser alloc] initWithData:data];
- [postParser setDelegate:self];
- self.posts = [NSMutableArray arrayWithCapacity:1];
- [postParser parse];
-
- NSMutableArray *objects = [NSMutableArray arrayWithCapacity:1];
-
- for (NSDictionary *post in self.posts) {
- NSString *postTags = [post objectForKey:@"tag"];
- if ([postTags rangeOfString:tag].location != NSNotFound) {
- QSObject *newObject = [self objectForPost:post];
- if (newObject) {
- [objects addObject:newObject];
- }
- }
+- (NSArray *)fetchBookmarksForTag:(NSString *)tag
+ site:(SocialSite)site
+ username:(NSString *)username
+ password:(NSString *)password
+ host:(NSString *)host {
+ NSData *data = [self cachedBookmarkDataForSite:site
+ username:username
+ host:host];
+ if (!data)
+ return @[];
+
+ NSXMLParser *postParser = [[NSXMLParser alloc] initWithData:data];
+ [postParser setDelegate:self];
+ self.posts = [NSMutableArray arrayWithCapacity:1];
+ [postParser parse];
+
+ NSMutableArray *objects = [NSMutableArray arrayWithCapacity:1];
+
+ for (NSDictionary *post in self.posts) {
+ NSString *postTags = [post objectForKey:@"tag"];
+ if ([postTags rangeOfString:tag].location != NSNotFound) {
+ QSObject *newObject = [self objectForPost:post];
+ if (newObject) {
+ [objects addObject:newObject];
+ }
}
-
- return objects;
+ }
+
+ return objects;
}
- (QSObject *)objectForPost:(NSDictionary *)post {
- QSObject *newObject = [QSObject makeObjectWithIdentifier:[post objectForKey:@"hash"]];
- [newObject setObject:[post objectForKey:@"href"] forType:QSURLType];
- [newObject setName:[post objectForKey:@"description"]];
- [newObject setDetails:[post objectForKey:@"extended"]];
- [newObject setPrimaryType:QSURLType];
- return newObject;
+ QSObject *newObject =
+ [QSObject makeObjectWithIdentifier:[post objectForKey:@"hash"]];
+ [newObject setObject:[post objectForKey:@"href"] forType:QSURLType];
+ [newObject setName:[post objectForKey:@"description"]];
+ [newObject setDetails:[post objectForKey:@"extended"]];
+ [newObject setPrimaryType:QSURLType];
+ return newObject;
}
#pragma mark - NSXMLParserDelegate
-- (void)parser:(NSXMLParser*)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
- if ([elementName isEqualToString:@"post"] && attributeDict) {
- [self.posts addObject:attributeDict];
- }
+- (void)parser:(NSXMLParser *)parser
+ didStartElement:(NSString *)elementName
+ namespaceURI:(NSString *)namespaceURI
+ qualifiedName:(NSString *)qName
+ attributes:(NSDictionary *)attributeDict {
+ if ([elementName isEqualToString:@"post"] && attributeDict) {
+ [self.posts addObject:attributeDict];
+ }
}
-- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
- // Implementation if needed
+- (void)parser:(NSXMLParser *)parser
+ didEndElement:(NSString *)elementName
+ namespaceURI:(NSString *)namespaceURI
+ qualifiedName:(NSString *)qName {
+ // Implementation if needed
}
@end
diff --git a/Providers/QSLinkdingProvider.h b/Providers/QSLinkdingProvider.h
index 28807e8..0537a0c 100644
--- a/Providers/QSLinkdingProvider.h
+++ b/Providers/QSLinkdingProvider.h
@@ -5,12 +5,15 @@
// Provider for Linkding API (JSON-based with API key)
//
-#import <Foundation/Foundation.h>
#import "QSBookmarkProvider.h"
+#import <Foundation/Foundation.h>
@interface QSLinkdingProvider : NSObject <QSBookmarkProvider>
-- (NSData *)cachedBookmarkDataForHost:(NSString *)host username:(NSString *)username;
-- (void)cacheBookmarkData:(NSData *)data forHost:(NSString *)host username:(NSString *)username;
+- (NSData *)cachedBookmarkDataForHost:(NSString *)host
+ username:(NSString *)username;
+- (void)cacheBookmarkData:(NSData *)data
+ forHost:(NSString *)host
+ username:(NSString *)username;
@end \ No newline at end of file
diff --git a/Providers/QSLinkdingProvider.m b/Providers/QSLinkdingProvider.m
index c7bf33f..378ae6f 100644
--- a/Providers/QSLinkdingProvider.m
+++ b/Providers/QSLinkdingProvider.m
@@ -4,178 +4,230 @@
//
#import "QSLinkdingProvider.h"
-#import "SocialSite.h"
#import "Constants.h"
+#import "SocialSite.h"
#import <QSCore/QSCore.h>
@implementation QSLinkdingProvider
-- (BOOL)canHandleSite:(SocialSite)site username:(NSString *)username password:(NSString *)password host:(NSString *)host {
- return (site == SocialSiteLinkding) &&
- username.length > 0 &&
- password.length > 0 && // password is API token for Linkding
- host.length > 0;
+- (BOOL)canHandleSite:(SocialSite)site
+ username:(NSString *)username
+ password:(NSString *)password
+ host:(NSString *)host {
+ return (site == SocialSiteLinkding) && username.length > 0 &&
+ password.length > 0 && // password is API token for Linkding
+ host.length > 0;
}
-- (NSData *)cachedBookmarkDataForHost:(NSString *)host username:(NSString *)username {
- // Create a safe filename from host
- NSString *safeHost = [[host componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@"-"];
- NSString *cachePath = [QSApplicationSupportSubPath([NSString stringWithFormat:@"Caches/linkding/"], NO) stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-%@.json", safeHost, username]];
- return [NSData dataWithContentsOfFile:cachePath];
+- (NSData *)cachedBookmarkDataForHost:(NSString *)host
+ username:(NSString *)username {
+ // Create a safe filename from host
+ NSString *safeHost =
+ [[host componentsSeparatedByCharactersInSet:[[NSCharacterSet
+ alphanumericCharacterSet]
+ invertedSet]]
+ componentsJoinedByString:@"-"];
+ NSString *cachePath = [QSApplicationSupportSubPath(
+ [NSString stringWithFormat:@"Caches/linkding/"], NO)
+ stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-%@.json",
+ safeHost,
+ username]];
+ return [NSData dataWithContentsOfFile:cachePath];
}
-- (void)cacheBookmarkData:(NSData *)data forHost:(NSString *)host username:(NSString *)username {
- // Create a safe filename from host
- NSString *safeHost = [[host componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@"-"];
- NSString *cachePath = [QSApplicationSupportSubPath([NSString stringWithFormat:@"Caches/linkding/"], YES) stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-%@.json", safeHost, username]];
- [data writeToFile:cachePath atomically:NO];
+- (void)cacheBookmarkData:(NSData *)data
+ forHost:(NSString *)host
+ username:(NSString *)username {
+ // Create a safe filename from host
+ NSString *safeHost =
+ [[host componentsSeparatedByCharactersInSet:[[NSCharacterSet
+ alphanumericCharacterSet]
+ invertedSet]]
+ componentsJoinedByString:@"-"];
+ NSString *cachePath = [QSApplicationSupportSubPath(
+ [NSString stringWithFormat:@"Caches/linkding/"], YES)
+ stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-%@.json",
+ safeHost,
+ username]];
+ [data writeToFile:cachePath atomically:NO];
}
-- (NSArray *)fetchBookmarksForSite:(SocialSite)site username:(NSString *)username password:(NSString *)password identifier:(NSString *)identifier host:(NSString *)host includeTags:(BOOL)includeTags {
-
- if (![self canHandleSite:site username:username password:password host:host]) {
- return @[];
+- (NSArray *)fetchBookmarksForSite:(SocialSite)site
+ username:(NSString *)username
+ password:(NSString *)password
+ identifier:(NSString *)identifier
+ host:(NSString *)host
+ includeTags:(BOOL)includeTags {
+
+ if (![self canHandleSite:site
+ username:username
+ password:password
+ host:host]) {
+ return @[];
+ }
+
+ // Try cached data first
+ NSData *data = [self cachedBookmarkDataForHost:host username:username];
+
+ // If no cached data, fetch from API
+ if (![data length]) {
+ // Construct Linkding API URL
+ NSString *baseURL = host;
+ if (![baseURL hasPrefix:@"http://"] && ![baseURL hasPrefix:@"https://"]) {
+ baseURL = [NSString stringWithFormat:@"https://%@", baseURL];
}
-
- // Try cached data first
- NSData *data = [self cachedBookmarkDataForHost:host username:username];
-
- // If no cached data, fetch from API
- if (![data length]) {
- // Construct Linkding API URL
- NSString *baseURL = host;
- if (![baseURL hasPrefix:@"http://"] && ![baseURL hasPrefix:@"https://"]) {
- baseURL = [NSString stringWithFormat:@"https://%@", baseURL];
- }
- if ([baseURL hasSuffix:@"/"]) {
- baseURL = [baseURL substringToIndex:[baseURL length] - 1];
- }
-
- NSString *urlString = [NSString stringWithFormat:@"%@/api/bookmarks/", baseURL];
- NSURL *requestURL = [NSURL URLWithString:urlString];
-
- if (!requestURL) {
- NSLog(@"Invalid Linkding host URL: %@", host);
- return @[];
- }
-
- NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:requestURL
- cachePolicy:NSURLRequestUseProtocolCachePolicy
- timeoutInterval:60.0];
- [theRequest setHTTPMethod:@"GET"];
- [theRequest setValue:@"application/json" forHTTPHeaderField:@"Accept"];
- [theRequest setValue:[NSString stringWithFormat:@"Token %@", password] forHTTPHeaderField:@"Authorization"];
- [theRequest setValue:@"Quicksilver (Blacktree,MacOSX)" forHTTPHeaderField:@"User-Agent"];
-
- NSError *error = nil;
- data = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:nil error:&error];
-
- if (error) {
- NSLog(@"Error fetching Linkding bookmarks: %@", error.localizedDescription);
- return @[];
- }
-
- // Cache the data
- [self cacheBookmarkData:data forHost:host username:username];
+ if ([baseURL hasSuffix:@"/"]) {
+ baseURL = [baseURL substringToIndex:[baseURL length] - 1];
}
-
- // Parse JSON data
- NSError *jsonError = nil;
- NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
-
- if (jsonError) {
- NSLog(@"Error parsing Linkding JSON: %@", jsonError.localizedDescription);
- return @[];
+
+ NSString *urlString =
+ [NSString stringWithFormat:@"%@/api/bookmarks/", baseURL];
+ NSURL *requestURL = [NSURL URLWithString:urlString];
+
+ if (!requestURL) {
+ NSLog(@"Invalid Linkding host URL: %@", host);
+ return @[];
}
-
- NSArray *results = [jsonResponse objectForKey:@"results"];
- if (!results || ![results isKindOfClass:[NSArray class]]) {
- NSLog(@"Invalid Linkding response format");
- return @[];
+
+ NSMutableURLRequest *theRequest =
+ [NSMutableURLRequest requestWithURL:requestURL
+ cachePolicy:NSURLRequestUseProtocolCachePolicy
+ timeoutInterval:60.0];
+ [theRequest setHTTPMethod:@"GET"];
+ [theRequest setValue:@"application/json" forHTTPHeaderField:@"Accept"];
+ [theRequest setValue:[NSString stringWithFormat:@"Token %@", password]
+ forHTTPHeaderField:@"Authorization"];
+ [theRequest setValue:@"Quicksilver (Blacktree,MacOSX)"
+ forHTTPHeaderField:@"User-Agent"];
+
+ NSError *error = nil;
+ data = [NSURLConnection sendSynchronousRequest:theRequest
+ returningResponse:nil
+ error:&error];
+
+ if (error) {
+ NSLog(@"Error fetching Linkding bookmarks: %@",
+ error.localizedDescription);
+ return @[];
}
-
- NSMutableArray *objects = [NSMutableArray arrayWithCapacity:1];
- NSMutableSet *tagSet = [NSMutableSet set];
-
- // Create bookmark objects
- for (NSDictionary *bookmark in results) {
- QSObject *newObject = [self objectForLinkdingBookmark:bookmark];
- if (newObject) {
- [objects addObject:newObject];
-
- // Collect tags if requested
- if (includeTags) {
- NSArray *tags = [bookmark objectForKey:@"tag_names"];
- if (tags && [tags isKindOfClass:[NSArray class]]) {
- [tagSet addObjectsFromArray:tags];
- }
- }
+
+ // Cache the data
+ [self cacheBookmarkData:data forHost:host username:username];
+ }
+
+ // Parse JSON data
+ NSError *jsonError = nil;
+ NSDictionary *jsonResponse =
+ [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
+
+ if (jsonError) {
+ NSLog(@"Error parsing Linkding JSON: %@", jsonError.localizedDescription);
+ return @[];
+ }
+
+ NSArray *results = [jsonResponse objectForKey:@"results"];
+ if (!results || ![results isKindOfClass:[NSArray class]]) {
+ NSLog(@"Invalid Linkding response format");
+ return @[];
+ }
+
+ NSMutableArray *objects = [NSMutableArray arrayWithCapacity:1];
+ NSMutableSet *tagSet = [NSMutableSet set];
+
+ // Create bookmark objects
+ for (NSDictionary *bookmark in results) {
+ QSObject *newObject = [self objectForLinkdingBookmark:bookmark];
+ if (newObject) {
+ [objects addObject:newObject];
+
+ // Collect tags if requested
+ if (includeTags) {
+ NSArray *tags = [bookmark objectForKey:@"tag_names"];
+ if (tags && [tags isKindOfClass:[NSArray class]]) {
+ [tagSet addObjectsFromArray:tags];
}
+ }
}
-
- // Create tag objects if requested
- if (includeTags) {
- for (NSString *tag in tagSet) {
- if (tag.length > 0) {
- QSObject *tagObject = [QSObject makeObjectWithIdentifier:[NSString stringWithFormat:@"[Linkding tag]:%@", tag]];
- [tagObject setObject:tag forType:kTagType];
- [tagObject setObject:@(site) forMeta:kTagSiteField];
- [tagObject setObject:username forMeta:kTagUsernameField];
- [tagObject setObject:host forMeta:kTagHostField];
- // We need the identifier to be able to fetch the keychain password
- [tagObject setObject:identifier forMeta:kTagIdentifierField];
- [tagObject setName:tag];
- [tagObject setPrimaryType:kTagType];
- [objects addObject:tagObject];
- }
- }
+ }
+
+ // Create tag objects if requested
+ if (includeTags) {
+ for (NSString *tag in tagSet) {
+ if (tag.length > 0) {
+ QSObject *tagObject = [QSObject
+ makeObjectWithIdentifier:[NSString
+ stringWithFormat:@"[Linkding tag]:%@",
+ tag]];
+ [tagObject setObject:tag forType:kTagType];
+ [tagObject setObject:@(site) forMeta:kTagSiteField];
+ [tagObject setObject:username forMeta:kTagUsernameField];
+ [tagObject setObject:host forMeta:kTagHostField];
+ // We need the identifier to be able to fetch the keychain password
+ [tagObject setObject:identifier forMeta:kTagIdentifierField];
+ [tagObject setName:tag];
+ [tagObject setPrimaryType:kTagType];
+ [objects addObject:tagObject];
+ }
}
-
- return objects;
+ }
+
+ return objects;
}
-- (NSArray *)fetchBookmarksForTag:(NSString *)tag site:(SocialSite)site username:(NSString *)username password:(NSString *)password host:(NSString *)host {
- NSData *data = [self cachedBookmarkDataForHost:host username:username];
- if (!data) return @[];
-
- NSError *jsonError;
- NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
-
- if (jsonError) return @[];
-
- NSArray *results = [jsonResponse objectForKey:@"results"];
- if (!results || ![results isKindOfClass:[NSArray class]]) return @[];
-
- NSMutableArray *objects = [NSMutableArray arrayWithCapacity:1];
-
- for (NSDictionary *bookmark in results) {
- NSArray *tags = [bookmark objectForKey:@"tag_names"];
- if (tags && [tags isKindOfClass:[NSArray class]] && [tags containsObject:tag]) {
- QSObject *newObject = [self objectForLinkdingBookmark:bookmark];
- if (newObject) {
- [objects addObject:newObject];
- }
- }
+- (NSArray *)fetchBookmarksForTag:(NSString *)tag
+ site:(SocialSite)site
+ username:(NSString *)username
+ password:(NSString *)password
+ host:(NSString *)host {
+ NSData *data = [self cachedBookmarkDataForHost:host username:username];
+ if (!data)
+ return @[];
+
+ NSError *jsonError;
+ NSDictionary *jsonResponse =
+ [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
+
+ if (jsonError)
+ return @[];
+
+ NSArray *results = [jsonResponse objectForKey:@"results"];
+ if (!results || ![results isKindOfClass:[NSArray class]])
+ return @[];
+
+ NSMutableArray *objects = [NSMutableArray arrayWithCapacity:1];
+
+ for (NSDictionary *bookmark in results) {
+ NSArray *tags = [bookmark objectForKey:@"tag_names"];
+ if (tags && [tags isKindOfClass:[NSArray class]] &&
+ [tags containsObject:tag]) {
+ QSObject *newObject = [self objectForLinkdingBookmark:bookmark];
+ if (newObject) {
+ [objects addObject:newObject];
+ }
}
-
- return objects;
+ }
+
+ return objects;
}
- (QSObject *)objectForLinkdingBookmark:(NSDictionary *)bookmark {
- NSNumber *bookmarkId = [bookmark objectForKey:@"id"];
- NSString *url = [bookmark objectForKey:@"url"];
- NSString *title = [bookmark objectForKey:@"title"];
- NSString *description = [bookmark objectForKey:@"description"];
-
- if (!bookmarkId || !url) return nil;
-
- QSObject *newObject = [QSObject makeObjectWithIdentifier:[NSString stringWithFormat:@"linkding-%@", bookmarkId]];
- [newObject setObject:url forType:QSURLType];
- [newObject setName:title.length > 0 ? title : url];
- [newObject setDetails:description.length > 0 ? description : @""];
- [newObject setPrimaryType:QSURLType];
-
- return newObject;
+ NSNumber *bookmarkId = [bookmark objectForKey:@"id"];
+ NSString *url = [bookmark objectForKey:@"url"];
+ NSString *title = [bookmark objectForKey:@"title"];
+ NSString *description = [bookmark objectForKey:@"description"];
+
+ if (!bookmarkId || !url)
+ return nil;
+
+ QSObject *newObject = [QSObject
+ makeObjectWithIdentifier:[NSString stringWithFormat:@"linkding-%@",
+ bookmarkId]];
+ [newObject setObject:url forType:QSURLType];
+ [newObject setName:title.length > 0 ? title : url];
+ [newObject setDetails:description.length > 0 ? description : @""];
+ [newObject setPrimaryType:QSURLType];
+
+ return newObject;
}
@end
diff --git a/QSDeliciousPlugIn_Source.h b/QSDeliciousPlugIn_Source.h
index 56456c5..26d613c 100644
--- a/QSDeliciousPlugIn_Source.h
+++ b/QSDeliciousPlugIn_Source.h
@@ -6,18 +6,18 @@
// Copyright __MyCompanyName__ 2004. All rights reserved.
//
-#import <Foundation/Foundation.h>
-#import <QSCore/QSCore.h>
-#import "SocialSite.h"
#import "QSBookmarkProvider.h"
#import "QSBookmarkProviderFactory.h"
+#import "SocialSite.h"
+#import <Foundation/Foundation.h>
+#import <QSCore/QSCore.h>
@interface QSDeliciousPlugIn_Source : QSObjectSource {
IBOutlet NSTextField *userField;
IBOutlet NSTextField *passField;
IBOutlet NSTextField *hostField;
}
-@property (nonatomic, strong) NSString *internalPassword;
+@property(nonatomic, strong) NSString *internalPassword;
- (IBAction)settingsChanged:(id)sender;
@end
diff --git a/QSDeliciousPlugIn_Source.m b/QSDeliciousPlugIn_Source.m
index 4d841a1..49526e1 100644
--- a/QSDeliciousPlugIn_Source.m
+++ b/QSDeliciousPlugIn_Source.m
@@ -18,28 +18,31 @@
// This method will get called whenever we change which
// active entry is selected.
- (void)setSelectedEntry:(id)selectedEntry {
- [super setSelectedEntry:selectedEntry];
- [self loadPasswordFromKeychain];
+ [super setSelectedEntry:selectedEntry];
+ [self loadPasswordFromKeychain];
}
#pragma mark - Quicksilver Source Methods
-- (BOOL)indexIsValidFromDate:(NSDate *)indexDate forEntry:(NSDictionary *)theEntry {
+- (BOOL)indexIsValidFromDate:(NSDate *)indexDate
+ forEntry:(NSDictionary *)theEntry {
return -[indexDate timeIntervalSinceNow] < 24 * 60 * 60;
}
-- (BOOL)isVisibleSource{
- return YES;
+- (BOOL)isVisibleSource {
+ return YES;
}
-- (NSImage *) iconForEntry:(NSDictionary *)dict {
- return [[NSBundle bundleForClass:[self class]]imageNamed:@"bookmark_icon"];
+- (NSImage *)iconForEntry:(NSDictionary *)dict {
+ return [[NSBundle bundleForClass:[self class]] imageNamed:@"bookmark_icon"];
}
-- (NSView *)settingsView
-{
+- (NSView *)settingsView {
if (![super settingsView]) {
- [[NSBundle bundleForClass:[self class]] loadNibNamed:NSStringFromClass([self class]) owner:self topLevelObjects:NULL];
+ [[NSBundle bundleForClass:[self class]]
+ loadNibNamed:NSStringFromClass([self class])
+ owner:self
+ topLevelObjects:NULL];
}
return [super settingsView];
}
@@ -48,7 +51,9 @@
- (SocialSite)siteIndex {
NSDictionary *settings = self.selectedEntry.sourceSettings;
- return [settings objectForKey:@"site"] != nil ? [[settings objectForKey:@"site"] integerValue] : SocialSiteDelicious;
+ return [settings objectForKey:@"site"] != nil
+ ? [[settings objectForKey:@"site"] integerValue]
+ : SocialSiteDelicious;
}
- (NSString *)currentUsername {
@@ -64,221 +69,251 @@
}
- (BOOL)includeTags {
- return [[self.selectedEntry.sourceSettings objectForKey:@"includeTags"] boolValue];
+ return [[self.selectedEntry.sourceSettings objectForKey:@"includeTags"]
+ boolValue];
}
-
// This method is called on action from all the NIB methods
// to force the catalog to save the current values.
- (IBAction)settingsChanged:(id)sender {
- [[NSNotificationCenter defaultCenter] postNotificationName:QSCatalogEntryChangedNotification object:self.selectedEntry];
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:QSCatalogEntryChangedNotification
+ object:self.selectedEntry];
[self willChangeValueForKey:@"isHostVisible"];
[self didChangeValueForKey:@"isHostVisible"];
}
-
#pragma mark - Keychain Helper Methods
- (NSString *)keychainKeyForIdentifier:(NSString *)identifier {
- return [NSString stringWithFormat:@"QSSocialBookmarks-%@", identifier];
+ return [NSString stringWithFormat:@"QSSocialBookmarks-%@", identifier];
}
- (NSString *)passwordFromKeychainForKey:(NSString *)key {
- const char *service = "QSSocialBookmarks";
- const char *account = [key UTF8String];
-
- UInt32 passwordLength = 0;
- void *passwordData = NULL;
-
- OSStatus status = SecKeychainFindGenericPassword(NULL,
- (UInt32)strlen(service), service,
- (UInt32)strlen(account), account,
- &passwordLength, &passwordData,
- NULL);
-
- if (status == errSecSuccess && passwordData != NULL) {
- NSString *password = [[NSString alloc] initWithBytes:passwordData
- length:passwordLength
- encoding:NSUTF8StringEncoding];
- SecKeychainItemFreeContent(NULL, passwordData);
- return password;
- }
-
- return nil;
+ const char *service = "QSSocialBookmarks";
+ const char *account = [key UTF8String];
+
+ UInt32 passwordLength = 0;
+ void *passwordData = NULL;
+
+ OSStatus status = SecKeychainFindGenericPassword(
+ NULL, (UInt32)strlen(service), service, (UInt32)strlen(account), account,
+ &passwordLength, &passwordData, NULL);
+
+ if (status == errSecSuccess && passwordData != NULL) {
+ NSString *password = [[NSString alloc] initWithBytes:passwordData
+ length:passwordLength
+ encoding:NSUTF8StringEncoding];
+ SecKeychainItemFreeContent(NULL, passwordData);
+ return password;
+ }
+
+ return nil;
}
-- (OSStatus)savePasswordToKeychainForKey:(NSString *)key password:(NSString *)password {
- const char *service = "QSSocialBookmarks";
- const char *account = [key UTF8String];
- const char *passwordCString = [password UTF8String];
-
- // First try to find existing item
- SecKeychainItemRef item = NULL;
- OSStatus findStatus = SecKeychainFindGenericPassword(NULL,
- (UInt32)strlen(service), service,
- (UInt32)strlen(account), account,
- NULL, NULL,
- &item);
-
- OSStatus status;
- if (findStatus == errSecSuccess) {
- // Update existing item
- status = SecKeychainItemModifyAttributesAndData(item,
- NULL,
- (UInt32)strlen(passwordCString),
- passwordCString);
- CFRelease(item);
- } else {
- // Create new item
- status = SecKeychainAddGenericPassword(NULL,
- (UInt32)strlen(service), service,
- (UInt32)strlen(account), account,
- (UInt32)strlen(passwordCString), passwordCString,
- NULL);
- }
-
- return status;
+- (OSStatus)savePasswordToKeychainForKey:(NSString *)key
+ password:(NSString *)password {
+ const char *service = "QSSocialBookmarks";
+ const char *account = [key UTF8String];
+ const char *passwordCString = [password UTF8String];
+
+ // First try to find existing item
+ SecKeychainItemRef item = NULL;
+ OSStatus findStatus = SecKeychainFindGenericPassword(
+ NULL, (UInt32)strlen(service), service, (UInt32)strlen(account), account,
+ NULL, NULL, &item);
+
+ OSStatus status;
+ if (findStatus == errSecSuccess) {
+ // Update existing item
+ status = SecKeychainItemModifyAttributesAndData(
+ item, NULL, (UInt32)strlen(passwordCString), passwordCString);
+ CFRelease(item);
+ } else {
+ // Create new item
+ status = SecKeychainAddGenericPassword(
+ NULL, (UInt32)strlen(service), service, (UInt32)strlen(account),
+ account, (UInt32)strlen(passwordCString), passwordCString, NULL);
+ }
+
+ return status;
}
- (OSStatus)deletePasswordFromKeychainForKey:(NSString *)key {
- const char *service = "QSSocialBookmarks";
- const char *account = [key UTF8String];
-
- SecKeychainItemRef item = NULL;
- OSStatus findStatus = SecKeychainFindGenericPassword(NULL,
- (UInt32)strlen(service), service,
- (UInt32)strlen(account), account,
- NULL, NULL,
- &item);
-
- if (findStatus == errSecSuccess) {
- OSStatus deleteStatus = SecKeychainItemDelete(item);
- CFRelease(item);
- return deleteStatus;
- }
-
- return findStatus;
+ const char *service = "QSSocialBookmarks";
+ const char *account = [key UTF8String];
+
+ SecKeychainItemRef item = NULL;
+ OSStatus findStatus = SecKeychainFindGenericPassword(
+ NULL, (UInt32)strlen(service), service, (UInt32)strlen(account), account,
+ NULL, NULL, &item);
+
+ if (findStatus == errSecSuccess) {
+ OSStatus deleteStatus = SecKeychainItemDelete(item);
+ CFRelease(item);
+ return deleteStatus;
+ }
+
+ return findStatus;
}
#pragma mark - Password Keychain Methods
- (void)loadPasswordFromKeychain {
- if (!self.selectedEntry || !self.selectedEntry.identifier) {
- self.internalPassword = nil;
- return;
- }
-
- NSString *keychainKey = [self keychainKeyForIdentifier:self.selectedEntry.identifier];
- [self setPassword: [self passwordFromKeychainForKey:keychainKey]];
+ if (!self.selectedEntry || !self.selectedEntry.identifier) {
+ self.internalPassword = nil;
+ return;
+ }
+
+ NSString *keychainKey =
+ [self keychainKeyForIdentifier:self.selectedEntry.identifier];
+ [self setPassword:[self passwordFromKeychainForKey:keychainKey]];
}
- (void)savePasswordToKeychain {
- if (!self.selectedEntry || !self.selectedEntry.identifier || !self.internalPassword) {
- return;
- }
-
- NSString *keychainKey = [self keychainKeyForIdentifier:self.selectedEntry.identifier];
- OSStatus status = [self savePasswordToKeychainForKey:keychainKey password:self.internalPassword];
-
- if (status != errSecSuccess) {
- NSLog(@"Failed to save password to keychain for key: %@, status: %d", keychainKey, (int)status);
- }
+ if (!self.selectedEntry || !self.selectedEntry.identifier ||
+ !self.internalPassword) {
+ return;
+ }
+
+ NSString *keychainKey =
+ [self keychainKeyForIdentifier:self.selectedEntry.identifier];
+ OSStatus status = [self savePasswordToKeychainForKey:keychainKey
+ password:self.internalPassword];
+
+ if (status != errSecSuccess) {
+ NSLog(@"Failed to save password to keychain for key: %@, status: %d",
+ keychainKey, (int)status);
+ }
}
#pragma mark - Password Property Accessors
- (NSString *)password {
- return self.internalPassword;
+ return self.internalPassword;
}
- (void)setPassword:(NSString *)password {
- self.internalPassword = password;
-
- // Save to keychain asynchronously
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- [self savePasswordToKeychain];
- });
+ self.internalPassword = password;
+
+ // Save to keychain asynchronously
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
+ ^{
+ [self savePasswordToKeychain];
+ });
}
#pragma mark - Host Visibility Control
-- (BOOL) isHostVisible {
- return [SocialSiteHelper hasVariableHost:[self siteIndex]];
+- (BOOL)isHostVisible {
+ return [SocialSiteHelper hasVariableHost:[self siteIndex]];
+}
+- (void)setIsHostVisible:(BOOL)isVisible {
}
-- (void)setIsHostVisible:(BOOL)isVisible { }
#pragma mark - Objects For Entry
- (NSArray *)objectsForEntry:(QSCatalogEntry *)theEntry {
-
- NSDictionary *settings = theEntry.sourceSettings;
-
- SocialSite site = [settings objectForKey:@"site"] != nil ? [[settings objectForKey:@"site"] integerValue] : SocialSiteDelicious;
- NSString *username = [settings objectForKey:@"username"];
- NSString *identifier = theEntry.identifier;
- NSString *keychainKey = [self keychainKeyForIdentifier:identifier];
- NSString *password = [self passwordFromKeychainForKey:keychainKey];
- NSString *host = [settings objectForKey:@"host"];
- BOOL includeTags = [settings objectForKey:@"includeTags"];
-
- QSBookmarkProviderFactory *factory = [QSBookmarkProviderFactory sharedFactory];
- id<QSBookmarkProvider> provider = [factory providerForSite:site username:username password:password host:host];
-
- if (!provider) {
- NSLog(@"No provider available for site %ld with username %@", (long)site, username);
- return @[];
- }
-
- return [provider fetchBookmarksForSite:site username:username password:password identifier:identifier host:host includeTags:includeTags];
-}
-- (NSArray *)objectsForTag:(NSString *)tag site:(SocialSite)site username:(NSString *)username identifier:(NSString *)identifier host:(NSString *)host {
-
+ NSDictionary *settings = theEntry.sourceSettings;
+
+ SocialSite site = [settings objectForKey:@"site"] != nil
+ ? [[settings objectForKey:@"site"] integerValue]
+ : SocialSiteDelicious;
+ NSString *username = [settings objectForKey:@"username"];
+ NSString *identifier = theEntry.identifier;
NSString *keychainKey = [self keychainKeyForIdentifier:identifier];
NSString *password = [self passwordFromKeychainForKey:keychainKey];
+ NSString *host = [settings objectForKey:@"host"];
+ BOOL includeTags = [settings objectForKey:@"includeTags"];
+
+ QSBookmarkProviderFactory *factory =
+ [QSBookmarkProviderFactory sharedFactory];
+ id<QSBookmarkProvider> provider = [factory providerForSite:site
+ username:username
+ password:password
+ host:host];
- QSBookmarkProviderFactory *factory = [QSBookmarkProviderFactory sharedFactory];
- id<QSBookmarkProvider> provider = [factory providerForSite:site username:username password:password host:host];
-
if (!provider) {
- NSLog(@"No provider available for site %ld with username %@", (long)site, username);
+ NSLog(@"No provider available for site %ld with username %@", (long)site,
+ username);
return @[];
}
-
- // Check if provider supports tag-based fetching
- if ([provider respondsToSelector:@selector(fetchBookmarksForTag:site:username:password:host:)]) {
- return [provider fetchBookmarksForTag:tag site:site username:username password:password host:host];
- }
-
+
+ return [provider fetchBookmarksForSite:site
+ username:username
+ password:password
+ identifier:identifier
+ host:host
+ includeTags:includeTags];
+}
+
+- (NSArray *)objectsForTag:(NSString *)tag
+ site:(SocialSite)site
+ username:(NSString *)username
+ identifier:(NSString *)identifier
+ host:(NSString *)host {
+
+ NSString *keychainKey = [self keychainKeyForIdentifier:identifier];
+ NSString *password = [self passwordFromKeychainForKey:keychainKey];
+
+ QSBookmarkProviderFactory *factory =
+ [QSBookmarkProviderFactory sharedFactory];
+ id<QSBookmarkProvider> provider = [factory providerForSite:site
+ username:username
+ password:password
+ host:host];
+
+ if (!provider) {
+ NSLog(@"No provider available for site %ld with username %@", (long)site,
+ username);
return @[];
+ }
+
+ // Check if provider supports tag-based fetching
+ if ([provider respondsToSelector:@selector
+ (fetchBookmarksForTag:site:username:password:host:)]) {
+ return [provider fetchBookmarksForTag:tag
+ site:site
+ username:username
+ password:password
+ host:host];
+ }
+
+ return @[];
}
#pragma mark - Object Handler Methods
- (void)setQuickIconForObject:(QSObject *)object {
if (@available(macOS 11.0, *)) {
- NSImage *image = [NSImage imageWithSystemSymbolName:@"tag" accessibilityDescription:@"Bookmark"];
+ NSImage *image = [NSImage imageWithSystemSymbolName:@"tag"
+ accessibilityDescription:@"Bookmark"];
[object setIcon:image];
} else {
- [object setIcon:[[NSBundle bundleForClass:[self class]]imageNamed:@"bookmark_icon"]];
+ [object setIcon:[[NSBundle bundleForClass:[self class]]
+ imageNamed:@"bookmark_icon"]];
}
}
-// All our objects will have children. URLs will have tags, and tags will have URLs.
-- (BOOL)objectHasChildren:(QSObject *) object { return YES; }
+// All our objects will have children. URLs will have tags, and tags will have
+// URLs.
+- (BOOL)objectHasChildren:(QSObject *)object {
+ return YES;
+}
// This will receive a tag object. Tag objects will have the
// source configuration in the meta: source.username,
// source.site, source.host and source.identifier.
- (BOOL)loadChildrenForObject:(QSObject *)object {
-
+
NSNumber *siteNumber = [object objectForMeta:@"source.site"];
if (!siteNumber) {
NSLog(@"The tag didn't have a valid site.");
return NO;
}
-
+
SocialSite site = [siteNumber integerValue];
-
+
NSString *username = [object objectForMeta:@"source.username"];
if (!username) {
NSLog(@"The tag didn't have a valid username.");
@@ -302,9 +337,13 @@
return NO;
}
- NSArray *children = [self objectsForTag:tag site:site username:username identifier:identifier host:host];
- [object setChildren:children];
- return YES;
+ NSArray *children = [self objectsForTag:tag
+ site:site
+ username:username
+ identifier:identifier
+ host:host];
+ [object setChildren:children];
+ return YES;
}
@end
diff --git a/Types/SocialSite.h b/Types/SocialSite.h
index 43155c7..2510594 100644
--- a/Types/SocialSite.h
+++ b/Types/SocialSite.h
@@ -8,11 +8,11 @@
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, SocialSite) {
- SocialSiteDelicious = 0,
- SocialSiteMagnolia = 1,
- SocialSitePinboard = 2,
- SocialSiteLinkding = 3,
- SocialSiteSelfHostedDeliciousCompatible = 4
+ SocialSiteDelicious = 0,
+ SocialSiteMagnolia = 1,
+ SocialSitePinboard = 2,
+ SocialSiteLinkding = 3,
+ SocialSiteSelfHostedDeliciousCompatible = 4
};
@interface SocialSiteHelper : NSObject
diff --git a/Types/SocialSite.m b/Types/SocialSite.m
index 369f86d..258386d 100644
--- a/Types/SocialSite.m
+++ b/Types/SocialSite.m
@@ -8,48 +8,48 @@
@implementation SocialSiteHelper
+ (NSString *)displayNameForSite:(SocialSite)site {
- switch (site) {
- case SocialSiteDelicious:
- return @"del.icio.us";
- case SocialSiteMagnolia:
- return @"ma.gnolia.com";
- case SocialSitePinboard:
- return @"Pinboard";
- case SocialSiteLinkding:
- return @"Linkding";
- case SocialSiteSelfHostedDeliciousCompatible:
- return @"Self-Hosted (Delicious Compatible)";
- default:
- return @"Unknown";
- }
+ switch (site) {
+ case SocialSiteDelicious:
+ return @"del.icio.us";
+ case SocialSiteMagnolia:
+ return @"ma.gnolia.com";
+ case SocialSitePinboard:
+ return @"Pinboard";
+ case SocialSiteLinkding:
+ return @"Linkding";
+ case SocialSiteSelfHostedDeliciousCompatible:
+ return @"Self-Hosted (Delicious Compatible)";
+ default:
+ return @"Unknown";
+ }
}
// This is used for caching key
+ (NSString *)cacheKeyForSite:(SocialSite)site {
- switch (site) {
- case SocialSiteDelicious:
- return @"del.icio.us";
- case SocialSiteMagnolia:
- return @"ma.gnolia.com";
- case SocialSitePinboard:
- return @"pinboard.in";
- case SocialSiteSelfHostedDeliciousCompatible:
- return @"self-hosted-delicious";
- case SocialSiteLinkding:
- return @"linkding";
- default:
- return nil;
- }
+ switch (site) {
+ case SocialSiteDelicious:
+ return @"del.icio.us";
+ case SocialSiteMagnolia:
+ return @"ma.gnolia.com";
+ case SocialSitePinboard:
+ return @"pinboard.in";
+ case SocialSiteSelfHostedDeliciousCompatible:
+ return @"self-hosted-delicious";
+ case SocialSiteLinkding:
+ return @"linkding";
+ default:
+ return nil;
+ }
}
+ (BOOL)hasVariableHost:(SocialSite)site {
- switch (site) {
- case SocialSiteSelfHostedDeliciousCompatible:
- case SocialSiteLinkding:
- return YES;
- default:
- return NO;
- }
+ switch (site) {
+ case SocialSiteSelfHostedDeliciousCompatible:
+ case SocialSiteLinkding:
+ return YES;
+ default:
+ return NO;
+ }
}
@end