From 31bd9d918c7de6ffd65069920e1f072ddce64243 Mon Sep 17 00:00:00 2001 From: Olof Hellman Date: Wed, 24 Jan 2018 00:06:34 -0800 Subject: [PATCH 1/3] Scripting support for articles and basic article properties Also, added support for accessing feeds directly from the top level container, essentially skipping account as a hierarchy level. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this change, a script like tell app “Evergreen” title of every article of feed "Six Colors" where read is true end tell produces the expected result. --- Evergreen.xcodeproj/project.pbxproj | 10 +- Evergreen/Resources/Evergreen.sdef | 52 ++++++++ .../Scriptability/Article+Scriptability.swift | 111 ++++++++++++++++++ .../Scriptability/Feed+Scriptability.swift | 10 ++ .../NSApplication+Scriptability.swift | 16 +++ xcconfig/Evergreen_target.xcconfig | 2 +- 6 files changed, 197 insertions(+), 4 deletions(-) create mode 100644 Evergreen/Scriptability/Article+Scriptability.swift diff --git a/Evergreen.xcodeproj/project.pbxproj b/Evergreen.xcodeproj/project.pbxproj index 1ec685d84..a610601f0 100644 --- a/Evergreen.xcodeproj/project.pbxproj +++ b/Evergreen.xcodeproj/project.pbxproj @@ -133,6 +133,7 @@ 84FB9A2F1EDCD6C4003D53B9 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */; }; 84FB9A301EDCD6C4003D53B9 /* Sparkle.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 84FF69B11FC3793300DC198E /* FaviconURLFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */; }; + D553738B20186C20006D8857 /* Article+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D553737C20186C1F006D8857 /* Article+Scriptability.swift */; }; D5558FD32002245C0066386B /* ScriptingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5558FD22002245C0066386B /* ScriptingTests.swift */; }; D5558FD5200225680066386B /* NSAppleEventDescriptor+UserRecordFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5558FD4200225680066386B /* NSAppleEventDescriptor+UserRecordFields.swift */; }; D5558FD9200228D30066386B /* AppleEventUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5558FD7200228B80066386B /* AppleEventUtils.swift */; }; @@ -585,6 +586,7 @@ 84F2D5391FC2308B00998D64 /* UnreadFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnreadFeed.swift; sourceTree = ""; }; 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Frameworks/Vendor/Sparkle.framework; sourceTree = SOURCE_ROOT; }; 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconURLFinder.swift; sourceTree = ""; }; + D553737C20186C1F006D8857 /* Article+Scriptability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Article+Scriptability.swift"; sourceTree = ""; }; D5558FD1200223F60066386B /* testGetURL.applescript */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.applescript; path = testGetURL.applescript; sourceTree = ""; }; D5558FD22002245C0066386B /* ScriptingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ScriptingTests.swift; path = EvergreenTests/ScriptingTests/ScriptingTests.swift; sourceTree = SOURCE_ROOT; }; D5558FD4200225680066386B /* NSAppleEventDescriptor+UserRecordFields.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "NSAppleEventDescriptor+UserRecordFields.swift"; path = "AppleEvents/NSAppleEventDescriptor+UserRecordFields.swift"; sourceTree = SOURCE_ROOT; }; @@ -1186,6 +1188,7 @@ isa = PBXGroup; children = ( D5907D962004B7EB005947E5 /* Account+Scriptability.swift */, + D553737C20186C1F006D8857 /* Article+Scriptability.swift */, D5A2678B20130ECF00A8D3C0 /* Author+Scriptability.swift */, D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */, D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */, @@ -1261,12 +1264,12 @@ TargetAttributes = { 849C645F1ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = M8L2WTLA8W; - ProvisioningStyle = Manual; + DevelopmentTeam = 6V7D786XTL; + ProvisioningStyle = Automatic; }; 849C64701ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = 9C84TZ7Q6Z; + DevelopmentTeam = 6V7D786XTL; ProvisioningStyle = Automatic; TestTargetID = 849C645F1ED37A5D003D8FC0; }; @@ -1596,6 +1599,7 @@ 849A97641ED9EB96007D329B /* SidebarOutlineView.swift in Sources */, D5A2678C20130ECF00A8D3C0 /* Author+Scriptability.swift in Sources */, 84F2D5371FC22FCC00998D64 /* PseudoFeed.swift in Sources */, + D553738B20186C20006D8857 /* Article+Scriptability.swift in Sources */, 845EE7C11FC2488C00854A1F /* SmartFeed.swift in Sources */, 84702AA41FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift in Sources */, D5907D7F2004AC00005947E5 /* NSApplication+Scriptability.swift in Sources */, diff --git a/Evergreen/Resources/Evergreen.sdef b/Evergreen/Resources/Evergreen.sdef index d1bd7233d..6f320a5df 100644 --- a/Evergreen/Resources/Evergreen.sdef +++ b/Evergreen/Resources/Evergreen.sdef @@ -24,6 +24,9 @@ + + + @@ -89,6 +92,9 @@ + + + @@ -122,6 +128,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $1.logicalDatePublished + }) + return sortedArticles.map { ScriptableArticle($0, container:self) } as NSArray + } } diff --git a/Evergreen/Scriptability/NSApplication+Scriptability.swift b/Evergreen/Scriptability/NSApplication+Scriptability.swift index d95a141ef..9ecc48ee0 100644 --- a/Evergreen/Scriptability/NSApplication+Scriptability.swift +++ b/Evergreen/Scriptability/NSApplication+Scriptability.swift @@ -8,6 +8,7 @@ import Cocoa import Account +import Data extension NSApplication : ScriptingObjectContainer { @@ -25,6 +26,21 @@ extension NSApplication : ScriptingObjectContainer { return accounts.map { ScriptableAccount($0) } as NSArray } + /* + accessing feeds from the application object skips the 'account' containment hierarchy + this allows a script like 'articles of feed "The Shape of Everything"' as a shorthand + for 'articles of feed "The Shape of Everything" of account "On My Mac"' + */ + @objc(feeds) + func feeds() -> NSArray { + let accounts = AccountManager.shared.accounts + let emptyFeeds:[Feed] = [] + let feeds = accounts.reduce(emptyFeeds) { (result, nthAccount) -> [Feed] in + let accountFeeds = nthAccount.children.flatMap { $0 as? Feed } + return result + accountFeeds + } + return feeds.map { ScriptableFeed($0, container:self) } as NSArray + } } diff --git a/xcconfig/Evergreen_target.xcconfig b/xcconfig/Evergreen_target.xcconfig index edd30621f..3ecba4950 100644 --- a/xcconfig/Evergreen_target.xcconfig +++ b/xcconfig/Evergreen_target.xcconfig @@ -26,7 +26,7 @@ PROVISIONING_PROFILE_SPECIFIER = // /Users/Shared/git/SharedXcodeSettings/DeveloperSettings.xcconfig // -#include "../../SharedXcodeSettings/DeveloperSettings.xcconfig" +#include? "../../SharedXcodeSettings/DeveloperSettings.xcconfig" ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES COMBINE_HIDPI_IMAGES = YES From ed9b8c1804afc325a90a8d8f20c6ac628510180b Mon Sep 17 00:00:00 2001 From: Olof Hellman Date: Wed, 24 Jan 2018 00:09:37 -0800 Subject: [PATCH 2/3] revert development Team changes in pbxproj --- Evergreen.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Evergreen.xcodeproj/project.pbxproj b/Evergreen.xcodeproj/project.pbxproj index a610601f0..b15176cf8 100644 --- a/Evergreen.xcodeproj/project.pbxproj +++ b/Evergreen.xcodeproj/project.pbxproj @@ -1264,12 +1264,12 @@ TargetAttributes = { 849C645F1ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = 6V7D786XTL; - ProvisioningStyle = Automatic; + DevelopmentTeam = M8L2WTLA8W; + ProvisioningStyle = Manual; }; 849C64701ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = 6V7D786XTL; + DevelopmentTeam = 9C84TZ7Q6Z; ProvisioningStyle = Automatic; TestTargetID = 849C645F1ED37A5D003D8FC0; }; From 903dff13d291267b74ff90dd4e79755e2ae5add2 Mon Sep 17 00:00:00 2001 From: Olof Hellman Date: Wed, 24 Jan 2018 00:27:56 -0800 Subject: [PATCH 3/3] Add tests for simple article property script, fix error in previous OPML xctest function (wrong name for test function). --- Evergreen.xcodeproj/project.pbxproj | 6 ++++++ Evergreen/Resources/Evergreen.sdef | 2 +- .../Scriptability/Article+Scriptability.swift | 15 ++++++++++++++- .../ScriptingTests/ScriptingTests.swift | 6 +++++- .../scripts/testTitleOfArticlesWhose.applescript | 10 ++++++++++ 5 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 EvergreenTests/ScriptingTests/scripts/testTitleOfArticlesWhose.applescript diff --git a/Evergreen.xcodeproj/project.pbxproj b/Evergreen.xcodeproj/project.pbxproj index b15176cf8..bb2c7ad50 100644 --- a/Evergreen.xcodeproj/project.pbxproj +++ b/Evergreen.xcodeproj/project.pbxproj @@ -134,6 +134,8 @@ 84FB9A301EDCD6C4003D53B9 /* Sparkle.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 84FF69B11FC3793300DC198E /* FaviconURLFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */; }; D553738B20186C20006D8857 /* Article+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D553737C20186C1F006D8857 /* Article+Scriptability.swift */; }; + D55373AF2018797C006D8857 /* testTitleOfArticlesWhose.applescript in Sources */ = {isa = PBXBuildFile; fileRef = D55373A02018797B006D8857 /* testTitleOfArticlesWhose.applescript */; }; + D55373B020187A05006D8857 /* testTitleOfArticlesWhose.applescript in CopyFiles */ = {isa = PBXBuildFile; fileRef = D55373A02018797B006D8857 /* testTitleOfArticlesWhose.applescript */; }; D5558FD32002245C0066386B /* ScriptingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5558FD22002245C0066386B /* ScriptingTests.swift */; }; D5558FD5200225680066386B /* NSAppleEventDescriptor+UserRecordFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5558FD4200225680066386B /* NSAppleEventDescriptor+UserRecordFields.swift */; }; D5558FD9200228D30066386B /* AppleEventUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5558FD7200228B80066386B /* AppleEventUtils.swift */; }; @@ -455,6 +457,7 @@ D5A2679D201313A200A8D3C0 /* testNameOfAuthors.applescript in CopyFiles */, D5F4EDE920075C6700B9E363 /* testNameAndUrlOfEveryFeed.applescript in CopyFiles */, D5F4EDEA20075C6700B9E363 /* testNameOfEveryFolder.applescript in CopyFiles */, + D55373B020187A05006D8857 /* testTitleOfArticlesWhose.applescript in CopyFiles */, D5907CA2200232AD005947E5 /* testGenericScript.applescript in CopyFiles */, D5907CA3200232AF005947E5 /* testGetURL.applescript in CopyFiles */, ); @@ -587,6 +590,7 @@ 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Frameworks/Vendor/Sparkle.framework; sourceTree = SOURCE_ROOT; }; 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconURLFinder.swift; sourceTree = ""; }; D553737C20186C1F006D8857 /* Article+Scriptability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Article+Scriptability.swift"; sourceTree = ""; }; + D55373A02018797B006D8857 /* testTitleOfArticlesWhose.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = testTitleOfArticlesWhose.applescript; sourceTree = ""; }; D5558FD1200223F60066386B /* testGetURL.applescript */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.applescript; path = testGetURL.applescript; sourceTree = ""; }; D5558FD22002245C0066386B /* ScriptingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ScriptingTests.swift; path = EvergreenTests/ScriptingTests/ScriptingTests.swift; sourceTree = SOURCE_ROOT; }; D5558FD4200225680066386B /* NSAppleEventDescriptor+UserRecordFields.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "NSAppleEventDescriptor+UserRecordFields.swift"; path = "AppleEvents/NSAppleEventDescriptor+UserRecordFields.swift"; sourceTree = SOURCE_ROOT; }; @@ -1159,6 +1163,7 @@ D5F4EDE720075C1800B9E363 /* testNameAndUrlOfEveryFeed.applescript */, D5A2679B201312F900A8D3C0 /* testNameOfAuthors.applescript */, D5F4EDD720075C1300B9E363 /* testNameOfEveryFolder.applescript */, + D55373A02018797B006D8857 /* testTitleOfArticlesWhose.applescript */, ); path = scripts; sourceTree = ""; @@ -1676,6 +1681,7 @@ files = ( D5558FD5200225680066386B /* NSAppleEventDescriptor+UserRecordFields.swift in Sources */, D5558FD9200228D30066386B /* AppleEventUtils.swift in Sources */, + D55373AF2018797C006D8857 /* testTitleOfArticlesWhose.applescript in Sources */, D5907CA1200232A1005947E5 /* testGetURL.applescript in Sources */, D5A267C120131B8300A8D3C0 /* testFeedOPML.applescript in Sources */, D5A2679C201312F900A8D3C0 /* testNameOfAuthors.applescript in Sources */, diff --git a/Evergreen/Resources/Evergreen.sdef b/Evergreen/Resources/Evergreen.sdef index 6f320a5df..fa47941f6 100644 --- a/Evergreen/Resources/Evergreen.sdef +++ b/Evergreen/Resources/Evergreen.sdef @@ -166,7 +166,7 @@ - + diff --git a/Evergreen/Scriptability/Article+Scriptability.swift b/Evergreen/Scriptability/Article+Scriptability.swift index 91b889385..d1b7aa977 100644 --- a/Evergreen/Scriptability/Article+Scriptability.swift +++ b/Evergreen/Scriptability/Article+Scriptability.swift @@ -11,7 +11,7 @@ import Account import Data @objc(ScriptableArticle) -class ScriptableArticle: NSObject, UniqueIdScriptingObject { +class ScriptableArticle: NSObject, UniqueIdScriptingObject, ScriptingObjectContainer { let article:Article let container:ScriptingObjectContainer @@ -41,7 +41,13 @@ class ScriptableArticle: NSObject, UniqueIdScriptingObject { var scriptingUniqueId:Any { return article.uniqueID } + + // MARK: --- ScriptingObjectContainer protocol --- + var scriptingClassDescription: NSScriptClassDescription { + return self.classDescription as! NSScriptClassDescription + } + // MARK: --- Scriptable properties --- @objc(url) @@ -108,4 +114,11 @@ class ScriptableArticle: NSObject, UniqueIdScriptingObject { var imageURL:String { return article.imageURL ?? "" } + + @objc(authors) + var authors:NSArray { + let articleAuthors = article.authors ?? [] + return articleAuthors.map { ScriptableAuthor($0, container:self) } as NSArray + } + } diff --git a/EvergreenTests/ScriptingTests/ScriptingTests.swift b/EvergreenTests/ScriptingTests/ScriptingTests.swift index 06fb60520..3b2c2767f 100644 --- a/EvergreenTests/ScriptingTests/ScriptingTests.swift +++ b/EvergreenTests/ScriptingTests/ScriptingTests.swift @@ -94,8 +94,12 @@ class ScriptingTests: XCTestCase { _ = doIndividualScript(filename: "testNameOfAuthors") } - func testNameOfAuthorsScript() { + func testFeedOPML() { _ = doIndividualScript(filename: "testFeedOPML") } + func testTitleOfArticlesWhoseScript() { + _ = doIndividualScript(filename: "testTitleOfArticlesWhose") + } + } diff --git a/EvergreenTests/ScriptingTests/scripts/testTitleOfArticlesWhose.applescript b/EvergreenTests/ScriptingTests/scripts/testTitleOfArticlesWhose.applescript new file mode 100644 index 000000000..3ad5cca94 --- /dev/null +++ b/EvergreenTests/ScriptingTests/scripts/testTitleOfArticlesWhose.applescript @@ -0,0 +1,10 @@ +-- this script just tests that no error was generated from the script +try + tell application "Evergreen" + title of every article of feed "Six Colors" where read is true + end tell +on error message + return {test_result:false, script_result:message} +end try + +return {test_result:true}