Out of the box, Hook can already create and support links to particular OmniPlan files.
The following pair of scripts (Open Item and Get Address) enable Hook to link to one or more specific tasks in OmniPlan:
The Hook name and link created is based on the task(s) selected in the OmniPlan GUI,
and the link created opens the specified file, and also restores selection of one or more particular tasks.
Get Address and Open Item scripts below
(Note that Get Name can be left blank - its function is covered within the Get Address script)
Get Address
(Copy the whole script, all the way down to "end map")
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
tell application "OmniPlan"
set ds to documents as list
if 0 < length of ds then
set fp to "omniplan://localhost" & my encodedPath(POSIX path of ((file of (item 1 of ds)) as alias))
set ws to (windows as list)
if 0 < length of ws then
set seln to (selected tasks of (item 1 of ws))
if missing value is not seln and 0 < length of seln then
script go
on |λ|(x)
id of x
end |λ|
end script
set taskIDs to "task/" & my intercalate(",%2520", my map(go, seln))
script titles
on |λ|(x)
name of x
end |λ|
end script
"[" & my intercalate(", ", my map(titles, seln)) & ¬
"](" & fp & taskIDs & ")"
else
"[" & name of item 1 of ds & "](" & fp & ")"
end if
else
"[" & name of item 1 of ds & "](" & fp & ")"
end if
else
""
end if
end tell
-- encodedPath :: FilePath -> Percent Encoded String
on encodedPath(fp)
tell current application
set charSet to URLPathAllowedCharacterSet of its NSCharacterSet
(stringByAddingPercentEncodingWithAllowedCharacters_(charSet) of ¬
stringWithString_(fp) of its NSString) as string
end tell
end encodedPath
-- intercalate :: String -> [String] -> String
on intercalate(delim, xs)
set {dlm, my text item delimiters} to ¬
{my text item delimiters, delim}
set str to xs as text
set my text item delimiters to dlm
str
end intercalate
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
-- 2nd class handler function lifted into 1st class script wrapper.
if script is class of f then
f
else
script
property |λ| : f
end script
end if
end mReturn
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
-- The list obtained by applying f
-- to each element of xs.
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
Open Item
tell application "OmniPlan"
activate
GetURL "$0"
end tell
It’s great to see fine-grained linking! That’s a very important direction. And I’m sure omniplan users will love that ability.
(I personally don’t use OmniPlan yet, but it’s been on my wish list for a long time, and I use the other OmniOutliner and OmniFocus on a daily basis, and OmniGraffle for most of my diagramming needs.)
You mention OmniOutliner Luc; any chance a similar script could be developed to link to individual OO topics? (And being able to target Nisus bookmarks would truly be the holy grail!)
Fine grained linking to OmniOutliner was one of the very first use cases I had in mind for this product, along with PDFs and web pages. And I for one would find it handy. (I may have mentioned elsewhere on the forum that we built a couple of products at SFU that did this for HTML and videos. One could link to/from specific time points and x,y coordinates of quicktime in “gStudy”. ) The target apps would need to have automation to auto-scroll, so it requires some discussion with other apps’ devs. Fine-grained linking between PDFs/HTML and editable note-type documents is a natural direction that one would expect Hook to go in .
In Cognitive Productivity for macOS , and on this forum,I think, I mention a work-around which is to generate unique IDs (from gear menu or any other means) and to paste them in respective locations of documents. I append # and ^ personally to link from #… to ID after Hook links.
With these scripts, I was able to link a task in OmniPlan to another file, e.g., an Obsidian note. After this, when I bring up Hook on the Obsidian note, I see that the OmniPlan task is linked. However, it does not work the other way around. When I bring up Hook by selecting the OmniPlan task, it says that no linked items are found. Is this the desired result?
Thanks @RobTrew, I 've used your script and it still works!
But need to add a “/” before “task/” in line "set taskIDs to “task/”
Here is the modified Get Address function for it to work. Hope this help
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
tell application "OmniPlan"
set ds to documents as list
if 0 < length of ds then
set fp to "omniplan://localhost" & my encodedPath(POSIX path of ((file of (item 1 of ds)) as alias))
set ws to (windows as list)
if 0 < length of ws then
set seln to (selected tasks of (item 1 of ws))
if missing value is not seln and 0 < length of seln then
script go
on |λ|(x)
id of x
end |λ|
end script
set taskIDs to "/task/" & my intercalate(",%2520", my map(go, seln))
script titles
on |λ|(x)
name of x
end |λ|
end script
"[" & my intercalate(", ", my map(titles, seln)) & ¬
"](" & fp & taskIDs & ")"
else
"[" & name of item 1 of ds & "](" & fp & ")"
end if
else
"[" & name of item 1 of ds & "](" & fp & ")"
end if
else
""
end if
end tell
-- encodedPath :: FilePath -> Percent Encoded String
on encodedPath(fp)
tell current application
set charSet to URLPathAllowedCharacterSet of its NSCharacterSet
(stringByAddingPercentEncodingWithAllowedCharacters_(charSet) of ¬
stringWithString_(fp) of its NSString) as string
end tell
end encodedPath
-- intercalate :: String -> [String] -> String
on intercalate(delim, xs)
set {dlm, my text item delimiters} to ¬
{my text item delimiters, delim}
set str to xs as text
set my text item delimiters to dlm
str
end intercalate
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
-- 2nd class handler function lifted into 1st class script wrapper.
if script is class of f then
f
else
script
property |λ| : f
end script
end if
end mReturn
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
-- The list obtained by applying f
-- to each element of xs.
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map