diff options
| author | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-09-08 16:04:35 +0200 |
|---|---|---|
| committer | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-09-08 16:04:35 +0200 |
| commit | cec89d9e844dc39b84778e1b88e74e23f9ca2e6a (patch) | |
| tree | 16e36fdc1263dff71510256a43dc0d972bf35c88 | |
| parent | a5e25cc05e5e33e114331cc826211c89adbcfa6a (diff) | |
WIP generic Delicious API compatible
| -rw-r--r-- | Factories/QSBookmarkProviderFactory.m | 10 | ||||
| -rw-r--r-- | Info.plist | 2 | ||||
| -rw-r--r-- | Protocols/QSBookmarkProvider.h | 15 | ||||
| -rw-r--r-- | Providers/QSDeliciousAPIProvider.h | 5 | ||||
| -rw-r--r-- | Providers/QSDeliciousAPIProvider.m | 93 | ||||
| -rw-r--r-- | Providers/QSLinkdingProvider.m | 25 | ||||
| -rw-r--r-- | QSDeliciousPlugIn.xcodeproj/project.pbxproj | 6 | ||||
| -rw-r--r-- | QSDeliciousPlugIn_Source.m | 59 | ||||
| -rw-r--r-- | QSDeliciousPlugIn_Source.xib | 64 | ||||
| -rw-r--r-- | Types/Constants.h | 9 | ||||
| -rw-r--r-- | Types/Constants.m | 8 | ||||
| -rw-r--r-- | Types/SocialSite.h | 7 | ||||
| -rw-r--r-- | Types/SocialSite.m | 22 |
13 files changed, 167 insertions, 158 deletions
diff --git a/Factories/QSBookmarkProviderFactory.m b/Factories/QSBookmarkProviderFactory.m index 6fcfdfb..75fd0e2 100644 --- a/Factories/QSBookmarkProviderFactory.m +++ b/Factories/QSBookmarkProviderFactory.m @@ -34,17 +34,13 @@ - (void)setupProviders { NSMutableArray *mutableProviders = [NSMutableArray array]; - // Create Pinboard API providers for each supported site - QSDeliciousAPIProvider *deliciousProvider = [[QSDeliciousAPIProvider alloc] initWithSite:SocialSiteDelicious]; - QSDeliciousAPIProvider *magnoliaProvider = [[QSDeliciousAPIProvider alloc] initWithSite:SocialSiteMagnolia]; - QSDeliciousAPIProvider *pinboardProvider = [[QSDeliciousAPIProvider alloc] initWithSite:SocialSitePinboard]; + // Create Delicious / Pinboard API providers for each supported site + QSDeliciousAPIProvider *deliciousAPIProvider = [[QSDeliciousAPIProvider alloc] init]; // Create Linkding provider QSLinkdingProvider *linkdingProvider = [[QSLinkdingProvider alloc] init]; - [mutableProviders addObject:deliciousProvider]; - [mutableProviders addObject:magnoliaProvider]; - [mutableProviders addObject:pinboardProvider]; + [mutableProviders addObject:deliciousAPIProvider]; [mutableProviders addObject:linkdingProvider]; self.providers = [mutableProviders copy]; @@ -42,7 +42,7 @@ </dict> <key>QSObjectHandlers</key> <dict> - <key>us.icio.del.tag</key> + <key>qs.tag.socialbookmark</key> <string>QSDeliciousPlugIn_Source</string> </dict> <key>QSObjectSources</key> diff --git a/Protocols/QSBookmarkProvider.h b/Protocols/QSBookmarkProvider.h index 136ea2d..9131e3c 100644 --- a/Protocols/QSBookmarkProvider.h +++ b/Protocols/QSBookmarkProvider.h @@ -24,25 +24,10 @@ */ - (NSArray *)fetchBookmarksForSite:(SocialSite)site username:(NSString *)username password:(NSString *)password identifier:(NSString *)identifier host:(NSString *)host includeTags:(BOOL)includeTags; -/** - * Get the supported site for this provider - */ -- (SocialSite)supportedSite; - -/** - * Get display name for this provider - */ -- (NSString *)providerName; - @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; -/** - * Get the tag URL type identifier for this provider - */ -- (NSString *)tagURLType; - @end diff --git a/Providers/QSDeliciousAPIProvider.h b/Providers/QSDeliciousAPIProvider.h index 0064264..08a0534 100644 --- a/Providers/QSDeliciousAPIProvider.h +++ b/Providers/QSDeliciousAPIProvider.h @@ -12,12 +12,9 @@ @interface QSDeliciousAPIProvider : NSObject <QSBookmarkProvider, NSXMLParserDelegate> @property (nonatomic, strong) NSMutableArray *posts; -@property (nonatomic, assign) SocialSite site; - -- (instancetype)initWithSite:(SocialSite)site; // Subclasses can override these methods -- (NSString *)apiURLForSite:(SocialSite)site; +- (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; - (void)cacheBookmarkData:(NSData *)data forSite:(SocialSite)site username:(NSString *)username; diff --git a/Providers/QSDeliciousAPIProvider.m b/Providers/QSDeliciousAPIProvider.m index ab09f88..31b283d 100644 --- a/Providers/QSDeliciousAPIProvider.m +++ b/Providers/QSDeliciousAPIProvider.m @@ -5,52 +5,59 @@ #import "QSDeliciousAPIProvider.h" #import "SocialSite.h" +#import "Constants.h" #import <QSCore/QSCore.h> @implementation QSDeliciousAPIProvider -- (instancetype)initWithSite:(SocialSite)site { - self = [super init]; - if (self) { - _site = site; - _posts = [NSMutableArray array]; - } - return self; -} - - (BOOL)canHandleSite:(SocialSite)site username:(NSString *)username password:(NSString *)password host:(NSString *)host { - return (site == self.site) && - username.length > 0 && + return ( + site == SocialSiteDelicious || + site == SocialSiteMagnolia || + site == SocialSitePinboard || + site == SocialSiteSelfHostedDeliciousCompatible + ) && + username.length > 0 && password.length > 0 && - (site != SocialSiteLinkding); // This provider doesn't handle Linkding -} - -- (SocialSite)supportedSite { - return self.site; + (site != SocialSiteSelfHostedDeliciousCompatible || host.length > 0); } -- (NSString *)providerName { - return [SocialSiteHelper displayNameForSite:self.site]; -} - -- (NSString *)apiURLForSite:(SocialSite)site { +- (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 @"api.pinboard.in/v1"; + 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; + } +} - (NSURL *)requestURLForSite:(SocialSite)site username:(NSString *)username password:(NSString *)password host:(NSString *)host { - NSString *apiURL = [self apiURLForSite:site]; + NSString *apiURL = [self apiURLForSite:site andHost: host]; if (!apiURL) return nil; - - NSString *urlString = [NSString stringWithFormat:@"https://%@:%@@%@/posts/all?", username, password, apiURL]; + + 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, username, password]; + } else { + urlString = [NSString stringWithFormat:@"https://%@:%@@%@/posts/all?", username, password, apiURL]; + } return [NSURL URLWithString:urlString]; } @@ -61,15 +68,12 @@ } - (void)cacheBookmarkData:(NSData *)data forSite:(SocialSite)site username:(NSString *)username { - NSString *siteURL = [SocialSiteHelper siteURLForSite:site]; - NSString *cachePath = [QSApplicationSupportSubPath([NSString stringWithFormat:@"Caches/%@/", siteURL], YES) stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.xml", username]]; + NSString *siteURL = [SocialSiteHelper siteURLForSite:site]; + NSString *safeURL = [[siteURL componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@"-"]; + NSString *cachePath = [QSApplicationSupportSubPath([NSString stringWithFormat:@"Caches/%@/", safeURL], YES) stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.xml", username]]; [data writeToFile:cachePath atomically:NO]; } -- (NSString *)tagURLType { - return [NSString stringWithFormat:@"tag.%@", [SocialSiteHelper reversedSiteURLForSite:self.site]]; -} - - (NSArray *)fetchBookmarksForSite:(SocialSite)site username:(NSString *)username password:(NSString *)password identifier:(NSString *)identifier host:(NSString *)host includeTags:(BOOL)includeTags { // Try cached data first @@ -79,7 +83,7 @@ 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]; @@ -87,7 +91,7 @@ [theRequest setValue:@"text/xml" forHTTPHeaderField:@"Content-type"]; [theRequest setValue:@"Quicksilver (Blacktree,MacOSX)" forHTTPHeaderField:@"User-Agent"]; - NSError *error; + NSError *error = nil; data = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:nil error:&error]; if (error) { @@ -98,6 +102,8 @@ // Cache the data [self cacheBookmarkData:data forSite:site username:username]; } + + NSLog(@"WE ARE READY TO PARSE"); // Parse XML data NSXMLParser *postParser = [[NSXMLParser alloc] initWithData:data]; @@ -105,6 +111,8 @@ self.posts = [NSMutableArray arrayWithCapacity:1]; [postParser parse]; + + NSLog(@"STILL READY: %ld", [self.posts count]); NSMutableArray *objects = [NSMutableArray arrayWithCapacity:1]; NSMutableSet *tagSet = [NSMutableSet set]; @@ -129,15 +137,16 @@ if (includeTags) { for (NSString *tag in tagSet) { if (tag.length > 0) { - QSObject *tagObject = [QSObject makeObjectWithIdentifier:[NSString stringWithFormat:@"[%@ tag]:%@", [self providerName], tag]]; - [tagObject setObject:tag forType:[self tagURLType]]; - [tagObject setObject:@(site) forMeta:@"source.site"]; - [tagObject setObject:username forMeta:@"source.username"]; - [tagObject setObject:host forMeta:@"source.host"]; - [tagObject setObject:identifier forMeta:@"source.identifier"]; - [tagObject setName:tag]; - [tagObject setPrimaryType:[self tagURLType]]; - [objects addObject:tagObject]; + 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]; } } } diff --git a/Providers/QSLinkdingProvider.m b/Providers/QSLinkdingProvider.m index 094f318..c7bf33f 100644 --- a/Providers/QSLinkdingProvider.m +++ b/Providers/QSLinkdingProvider.m @@ -5,6 +5,7 @@ #import "QSLinkdingProvider.h" #import "SocialSite.h" +#import "Constants.h" #import <QSCore/QSCore.h> @implementation QSLinkdingProvider @@ -16,18 +17,6 @@ host.length > 0; } -- (SocialSite)supportedSite { - return SocialSiteLinkding; -} - -- (NSString *)providerName { - return @"Linkding"; -} - -- (NSString *)tagURLType { - return @"tag.linkding"; -} - - (NSData *)cachedBookmarkDataForHost:(NSString *)host username:(NSString *)username { // Create a safe filename from host NSString *safeHost = [[host componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@"-"]; @@ -129,14 +118,14 @@ for (NSString *tag in tagSet) { if (tag.length > 0) { QSObject *tagObject = [QSObject makeObjectWithIdentifier:[NSString stringWithFormat:@"[Linkding tag]:%@", tag]]; - [tagObject setObject:tag forType:[self tagURLType]]; - [tagObject setObject:@(site) forMeta:@"source.site"]; - [tagObject setObject:username forMeta:@"source.username"]; - [tagObject setObject:host forMeta:@"source.host"]; + [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:@"source.identifier"]; + [tagObject setObject:identifier forMeta:kTagIdentifierField]; [tagObject setName:tag]; - [tagObject setPrimaryType:[self tagURLType]]; + [tagObject setPrimaryType:kTagType]; [objects addObject:tagObject]; } } diff --git a/QSDeliciousPlugIn.xcodeproj/project.pbxproj b/QSDeliciousPlugIn.xcodeproj/project.pbxproj index d68169d..803ae6e 100644 --- a/QSDeliciousPlugIn.xcodeproj/project.pbxproj +++ b/QSDeliciousPlugIn.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 7FB0DC2F0B91FFC600A5B6FF /* QSCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FB0DC2D0B91FFC500A5B6FF /* QSCore.framework */; }; 7FB0DC300B91FFC600A5B6FF /* QSFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FB0DC2E0B91FFC600A5B6FF /* QSFoundation.framework */; }; 8D1AC9700486D14A00FE50C9 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD92D38A0106425D02CA0E72 /* Cocoa.framework */; }; + B59368092E6F04E200DBD0F1 /* Constants.m in Sources */ = {isa = PBXBuildFile; fileRef = B59368082E6F04DF00DBD0F1 /* Constants.m */; }; B5CF7D6A2E6B7631008A0EE6 /* QSDeliciousPlugIn_Source.xib in Resources */ = {isa = PBXBuildFile; fileRef = B5CF7D692E6B74FE008A0EE6 /* QSDeliciousPlugIn_Source.xib */; }; B5CF7D6E2E6B7A53008A0EE6 /* QSDeliciousAPIProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = B5CF7D602E6B73A0008A0EE6 /* QSDeliciousAPIProvider.m */; }; B5CF7D6F2E6B7A53008A0EE6 /* QSBookmarkProviderFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = B5CF7D642E6B73DA008A0EE6 /* QSBookmarkProviderFactory.m */; }; @@ -33,6 +34,8 @@ 7FB0DC2E0B91FFC600A5B6FF /* QSFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QSFoundation.framework; path = /Applications/Quicksilver.app/Contents/Frameworks/QSFoundation.framework; sourceTree = "<absolute>"; }; 8D1AC9730486D14A00FE50C9 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; }; 8D1AC9740486D14A00FE50C9 /* Social Bookmarks Plugin.qsplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Social Bookmarks Plugin.qsplugin"; sourceTree = BUILT_PRODUCTS_DIR; }; + B59368072E6F021E00DBD0F1 /* Constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = "<group>"; }; + B59368082E6F04DF00DBD0F1 /* Constants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Constants.m; sourceTree = "<group>"; }; B5CF7D5C2E6B7370008A0EE6 /* SocialSite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SocialSite.h; sourceTree = "<group>"; }; B5CF7D5D2E6B7377008A0EE6 /* SocialSite.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SocialSite.m; sourceTree = "<group>"; }; B5CF7D5E2E6B737E008A0EE6 /* QSBookmarkProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = QSBookmarkProvider.h; sourceTree = "<group>"; }; @@ -153,6 +156,8 @@ B59368042E6EC65000DBD0F1 /* Types */ = { isa = PBXGroup; children = ( + B59368072E6F021E00DBD0F1 /* Constants.h */, + B59368082E6F04DF00DBD0F1 /* Constants.m */, B5CF7D5C2E6B7370008A0EE6 /* SocialSite.h */, B5CF7D5D2E6B7377008A0EE6 /* SocialSite.m */, ); @@ -288,6 +293,7 @@ B5CF7D702E6B7A53008A0EE6 /* QSLinkdingProvider.m in Sources */, B5CF7D712E6B7A53008A0EE6 /* SocialSite.m in Sources */, E182BCD406FC8203007BF2C2 /* QSDeliciousPlugIn_Source.m in Sources */, + B59368092E6F04E200DBD0F1 /* Constants.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/QSDeliciousPlugIn_Source.m b/QSDeliciousPlugIn_Source.m index 55eedf0..4d841a1 100644 --- a/QSDeliciousPlugIn_Source.m +++ b/QSDeliciousPlugIn_Source.m @@ -7,9 +7,9 @@ // #import "QSDeliciousPlugIn_Source.h" +#import "Constants.h" #import <QSCore/QSCore.h> #import <Security/Security.h> -#import <os/log.h> @implementation QSDeliciousPlugIn_Source @@ -72,6 +72,8 @@ // to force the catalog to save the current values. - (IBAction)settingsChanged:(id)sender { [[NSNotificationCenter defaultCenter] postNotificationName:QSCatalogEntryChangedNotification object:self.selectedEntry]; + [self willChangeValueForKey:@"isHostVisible"]; + [self didChangeValueForKey:@"isHostVisible"]; } @@ -198,6 +200,12 @@ }); } +#pragma mark - Host Visibility Control +- (BOOL) isHostVisible { + return [SocialSiteHelper hasVariableHost:[self siteIndex]]; +} +- (void)setIsHostVisible:(BOOL)isVisible { } + #pragma mark - Objects For Entry - (NSArray *)objectsForEntry:(QSCatalogEntry *)theEntry { @@ -219,23 +227,22 @@ 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 username:(NSString *)username { - SocialSite site = [self siteIndex]; - NSString *password = [self currentPassword]; - NSString *host = [self currentHost]; - - // Get the appropriate provider using the factory - QSBookmarkProviderFactory *factory = [QSBookmarkProviderFactory sharedFactory]; - id<QSBookmarkProvider> provider = [factory providerForSite:site username:username password:password host:host]; +- (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 @[]; - } + 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:)]) { @@ -248,9 +255,17 @@ #pragma mark - Object Handler Methods - (void)setQuickIconForObject:(QSObject *)object { - [object setIcon:[[NSBundle bundleForClass:[self class]]imageNamed:@"bookmark_icon"]]; + if (@available(macOS 11.0, *)) { + NSImage *image = [NSImage imageWithSystemSymbolName:@"tag" accessibilityDescription:@"Bookmark"]; + [object setIcon:image]; + } else { + [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; } + // 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. @@ -281,21 +296,13 @@ return NO; } - NSString *tagType = nil; - if (site == SocialSiteLinkding) { - tagType = @"tag.linkding"; - } else { - NSString *reversedURL = [SocialSiteHelper reversedSiteURLForSite:site]; - tagType = [NSString stringWithFormat:@"tag.%@", reversedURL]; - } - - NSString *tag = [object objectForType:tagType]; + NSString *tag = [object objectForType:kTagType]; if (!tag) { NSLog(@"We could not find a valid tag type."); return NO; } - - NSArray *children = [self objectsForTag:tag username:username]; + + NSArray *children = [self objectsForTag:tag site:site username:username identifier:identifier host:host]; [object setChildren:children]; return YES; } diff --git a/QSDeliciousPlugIn_Source.xib b/QSDeliciousPlugIn_Source.xib index c688346..5f56ddc 100644 --- a/QSDeliciousPlugIn_Source.xib +++ b/QSDeliciousPlugIn_Source.xib @@ -33,7 +33,7 @@ </textFieldCell> </textField> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" id="username-label"> - <rect key="frame" x="18" y="179" width="70" height="16"/> + <rect key="frame" x="18" y="147" width="70" height="16"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <constraints> <constraint firstAttribute="height" constant="16" id="y8T-Rp-nV6"/> @@ -45,7 +45,7 @@ </textFieldCell> </textField> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" id="password-label"> - <rect key="frame" x="22" y="140" width="66" height="16"/> + <rect key="frame" x="22" y="108" width="66" height="16"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <constraints> <constraint firstAttribute="height" constant="16" id="BpN-ax-hN6"/> @@ -57,7 +57,7 @@ </textFieldCell> </textField> <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" id="host-label"> - <rect key="frame" x="51" y="101" width="37" height="16"/> + <rect key="frame" x="51" y="69" width="37" height="16"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <constraints> <constraint firstAttribute="height" constant="16" id="SmD-SH-xQe"/> @@ -67,31 +67,14 @@ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> </textFieldCell> - </textField> - <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" id="host-help-label"> - <rect key="frame" x="94" y="75" width="137" height="14"/> - <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> - <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="Required for Linkding" id="host-help-label-cell"> - <font key="font" metaFont="smallSystem"/> - <color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/> - <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> - </textFieldCell> - </textField> - <button id="include-tags-checkbox"> - <rect key="frame" x="96" y="52" width="99" height="16"/> - <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> - <buttonCell key="cell" type="check" title="Include Tags" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="include-tags-checkbox-cell"> - <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> - <font key="font" metaFont="system"/> - </buttonCell> - <constraints> - <constraint firstAttribute="height" constant="16" id="egr-kg-1on"/> - </constraints> <connections> - <action selector="settingsChanged:" target="-2" id="BfD-Jh-b7j"/> - <binding destination="-2" name="value" keyPath="selectedEntry.sourceSettings.includeTags" id="lGp-jw-qTE"/> + <binding destination="-2" name="hidden" keyPath="isHostVisible" id="IaX-Z5-bel"> + <dictionary key="options"> + <string key="NSValueTransformerName">NSNegateBoolean</string> + </dictionary> + </binding> </connections> - </button> + </textField> <popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="site-popup"> <rect key="frame" x="96" y="214" width="133" height="24"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/> @@ -103,7 +86,8 @@ <menuItem title="del.icio.us" state="on" id="site-delicious"/> <menuItem title="ma.gnolia.com" tag="1" id="site-magnolia"/> <menuItem title="Pinboard" tag="2" id="site-pinboard"/> - <menuItem title="Linkding" tag="3" id="site-linkding"/> + <menuItem title="Linkding" tag="3" id="NKh-uv-BH6"/> + <menuItem title="Self-Hosted (Delicious API)" tag="4" id="site-linkding"/> </items> </menu> </popUpButtonCell> @@ -113,7 +97,7 @@ </connections> </popUpButton> <textField focusRingType="none" verticalHuggingPriority="750" id="username-text-field"> - <rect key="frame" x="96" y="175" width="133" height="24"/> + <rect key="frame" x="96" y="143" width="133" height="24"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/> <constraints> <constraint firstAttribute="height" constant="24" id="fas-E9-Pf6"/> @@ -129,7 +113,7 @@ </connections> </textField> <secureTextField focusRingType="none" verticalHuggingPriority="750" id="password-text-field"> - <rect key="frame" x="96" y="136" width="133" height="24"/> + <rect key="frame" x="96" y="104" width="133" height="24"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/> <constraints> <constraint firstAttribute="height" constant="24" id="dQF-Rc-KB8"/> @@ -148,7 +132,7 @@ </connections> </secureTextField> <textField focusRingType="none" verticalHuggingPriority="750" id="host-text-field"> - <rect key="frame" x="96" y="97" width="133" height="24"/> + <rect key="frame" x="96" y="65" width="133" height="24"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/> <constraints> <constraint firstAttribute="height" constant="24" id="bfW-V3-8A5"/> @@ -160,9 +144,29 @@ </textFieldCell> <connections> <action selector="settingsChanged:" target="-2" id="4GR-6l-ZnG"/> + <binding destination="-2" name="hidden" keyPath="isHostVisible" id="0Uj-hV-LYQ"> + <dictionary key="options"> + <string key="NSValueTransformerName">NSNegateBoolean</string> + </dictionary> + </binding> <binding destination="-2" name="value" keyPath="selectedEntry.sourceSettings.host" id="7qk-HC-e8v"/> </connections> </textField> + <button id="include-tags-checkbox"> + <rect key="frame" x="96" y="183" width="99" height="16"/> + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> + <buttonCell key="cell" type="check" title="Include Tags" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="include-tags-checkbox-cell"> + <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> + <font key="font" metaFont="system"/> + </buttonCell> + <constraints> + <constraint firstAttribute="height" constant="16" id="egr-kg-1on"/> + </constraints> + <connections> + <action selector="settingsChanged:" target="-2" id="BfD-Jh-b7j"/> + <binding destination="-2" name="value" keyPath="selectedEntry.sourceSettings.includeTags" id="lGp-jw-qTE"/> + </connections> + </button> </subviews> <point key="canvasLocation" x="155.5" y="180"/> </customView> diff --git a/Types/Constants.h b/Types/Constants.h new file mode 100644 index 0000000..75dd240 --- /dev/null +++ b/Types/Constants.h @@ -0,0 +1,9 @@ + +#pragma mark - Tag QSObject Constants + +extern NSString *const kTagType; + +extern NSString *const kTagSiteField; +extern NSString *const kTagUsernameField; +extern NSString *const kTagHostField; +extern NSString *const kTagIdentifierField; diff --git a/Types/Constants.m b/Types/Constants.m new file mode 100644 index 0000000..9300b54 --- /dev/null +++ b/Types/Constants.m @@ -0,0 +1,8 @@ +#pragma mark - Tag QSObject Constants + +NSString *const kTagType = @"qs.tag.socialbookmark"; + +NSString *const kTagSiteField = @"source.site"; +NSString *const kTagUsernameField = @"source.username"; +NSString *const kTagHostField = @"source.host"; +NSString *const kTagIdentifierField = @"source.identifier"; diff --git a/Types/SocialSite.h b/Types/SocialSite.h index f2c848c..130bc40 100644 --- a/Types/SocialSite.h +++ b/Types/SocialSite.h @@ -11,13 +11,14 @@ typedef NS_ENUM(NSInteger, SocialSite) { SocialSiteDelicious = 0, SocialSiteMagnolia = 1, SocialSitePinboard = 2, - SocialSiteLinkding = 3 + SocialSiteLinkding = 3, + SocialSiteSelfHostedDeliciousCompatible = 4 }; @interface SocialSiteHelper : NSObject + (NSString *)displayNameForSite:(SocialSite)site; + (NSString *)siteURLForSite:(SocialSite)site; -+ (NSString *)reversedSiteURLForSite:(SocialSite)site; ++ (BOOL)hasVariableHost:(SocialSite)site; -@end
\ No newline at end of file +@end diff --git a/Types/SocialSite.m b/Types/SocialSite.m index d002235..b73533f 100644 --- a/Types/SocialSite.m +++ b/Types/SocialSite.m @@ -17,6 +17,8 @@ return @"Pinboard"; case SocialSiteLinkding: return @"Linkding"; + case SocialSiteSelfHostedDeliciousCompatible: + return @"Self-Hosted (Delicious Compatible)"; default: return @"Unknown"; } @@ -30,6 +32,7 @@ return @"ma.gnolia.com"; case SocialSitePinboard: return @"pinboard.in"; + case SocialSiteSelfHostedDeliciousCompatible: case SocialSiteLinkding: return @""; // Will be provided by user as host default: @@ -37,19 +40,14 @@ } } -+ (NSString *)reversedSiteURLForSite:(SocialSite)site { ++ (BOOL)hasVariableHost:(SocialSite)site { switch (site) { - case SocialSiteDelicious: - return @"us.icio.del"; - case SocialSiteMagnolia: - return @"com.gnolia.ma"; - case SocialSitePinboard: - return @"in.pinboard"; - case SocialSiteLinkding: - return @"linkding"; // Generic identifier - default: - return nil; + case SocialSiteSelfHostedDeliciousCompatible: + case SocialSiteLinkding: + return YES; + default: + return NO; } } -@end
\ No newline at end of file +@end |