Integrating Omnifocus 2 with Devonthink Pro (Rob Trew scripts)


#1

I was using the scripts Rob Trew wrote for integrating Omnifocus 1 with Devonthink Pro (OpenProjFolderInDevn.scptd, OpenProjNOTESInDevn214.scptd). Now with the iPhone/iPad version of DTTG released I want to switch again from Evernote to Devonthink for my reference database.

I am currently working with Omnifocus 2 (2.6.1) and the scripts aren’t working with DTPO version 2.9.1

After first solving the database path issue I get an error on "cannot access “set «class FCvm» to “project””. The scripts are here: https://github.com/RobTrew/tree-tools/tree/master/DevonThink%20scripts

Unfortunately I cannot solve the problem. I have contacted Rob Trew and he is indicating this is an OF 1 script, and that the OF 2 object model is different.

Is there anybody out there who can help or has the scripts working?

Help is deeply appreciated because I want to use both wonderful products together.


#2

I am having similar problems, has any one developed a script which calls an Omnifocus quick entry window with a link back to the DEVONThink document?


#3

I’m facing the same issue. Trying to integrate Deconthink w/ OF2 –> can’t find any available scripts and I’m not good enough at this to write my own. Has anyone built any or found a software similar to Devonthink that integrates better with OF2?

Essentially I want my OF2 project structure to seamlessly mirror my file structure.


#4

What do you have in mind? Example: You need a link in the notes of a OmniFocus Project to a DT Folder? You need to replicate the folder structure of a DT database in OF?
Maybe I can help.


#5

Maybe we can do this in pieces. I’ve crossposted to the DEVONthink forum for help. Here’s a hack of Rob’s script that will create a project in DTPO according to the tree in OF2. Whats missing is the creation of the link back to DTPO in the OF2 project note. The DTPO group does have a link to the OF2 project, however.

@unlocked2412, Rob’s original scripts can be found here.

[code]property pblnJustFolder : true
property pblnUseSyncAsRoot : false

– Robin Trew

– ver .204 June 19 2011
– Aug 22 2011
– Ver .208 Still defaults to the DT Database named in pstrDTDB (below)
– but can use different DT databases for different OF folders
– (will use the first existing DT database whose POSIX path is found in the note of an enclosing OmniFocus folder
– [a script for editing OF folder notes can be found at
http://forums.omnigroup.com/showthread.php?t=21942]
– ver .211 Sep 12 2011
– Adds option, above - top of script, to make the Sync group the root of all new folders

– Disclaimer
– This is just a rough draft of something which I have sketched for my own personal use,
– and which is provided purely as an illustration of possible approaches to coding.
– You are free to adapt and reuse any part of it, without any warranties, implied
– or explicit, as to its behaviour or suitability for use

– IF property pblnJustFolder : false (see top of script)
– CREATES/OPENS A PROJECT NOTES FILE (STORED IN DEVONthink 2) FOR THE SELECTED OMNIFOCUS PROJECT.
– 1. The project notes file is an OmniOutliner 5 document stored in DEVONthink 2 folder
– 2. The Devonthink record contains a hyperlink back to the OmniFocus project
– 2. The script ensures that a DT hyperlink to the document in DevonThink is placed in the note field of the project
– 3. In the absence of a link to existing notes in DevonThink,
– the script will seek or create the OO5 file in a DevonThink folder which matches
– the folder path of the project in OmniFocus
– (and add the hyperlinks from project to notes, and from folder and notes to project)

– OR - IF property pblnJustFolder : true (see top of script)
– JUST CREATES/OPENS A PROJECT MATERIALS FOLDER (IN DEVONthink 2)
– FOR THE SELECTED OMNIFOCUS PROJECT.
– 1. The script ensures that a DT hyperlink to the document in DevonThink is placed in
– the note field of the project
– 2. In the absence of a link to an existing folder in DevonThink,
– the script will add the hyperlinks from project to folder, and from folder to project)

– Acknowledgements
– Inspired by Jim Harrison’s excellent scripts, which use the Finder rather than DEVONthink 2
http://jhh.med.virginia.edu/main/OmniFocusScripts
– The icon attached to this file is from the Float collection by Corey Marion
http://iconfactory.com/freeware/preview/flot

– GLOBAL CONSTANTS
– Initially assumes that project folders will be maintained in
– a database named [UserName]/Documents/Omnifocus Notes.
– Edit the name and path below to change the location and/or name
–of the main Projects folder that will contain the individual project folders

property pstrDTDB : “OmniFocus Notes” – name of default Devonthink Database
property pstrDTsuffix : “.dtBase2”
property pstrDocsPath : (path to documents folder as string) – path to ~/Documents
property pstrSync : “/Sync”

property pstrOO5Template : “Default”
property pstrOO5Suffix : “.ooutline”
property pstrOFPrefix : “omnifocus:///task/”
property pstrXMLPrefix : “<value key=“link”>”
property pstrRunDelim : “”
property pstrDTPrefix : “x-devonthink-item://”
property pstrDBPath : “$HOME/Library/Containers/com.omnigroup.OmniFocus2/Data/Library/Caches/com.omnigroup.OmniFocus2/OmniFocusDatabase2”
property pstrFolderLinkTitle : “[Devonthink folder for this project]”
property pstrNoteLinkTitle : “[Devonthink ooutline notes]”
property plngURLchars : 36

on run
– IS A PROJECT (OR ONE OF ITS TASKS) SELECTED IN OMNIFOCUS ?
set {oProject, strProjName, strProjID} to GetSeldProject()
if oProject is missing value then return

-- IS THERE A RELEVANT LINK IN THE NOTE FIELD OF THE PROJECT ?
set {strFolderURL, stroo5URL} to DTLinksInNote(oProject)

-- AND IF SO, DOES IT LEAD ANYWHERE ?
if pblnJustFolder then
	if strFolderURL ≠ "" then if FollowDTLink(strFolderURL) then return
else
	if stroo5URL ≠ "" then if FollowDTLink(stroo5URL) then return
end if

-- IN THE ABSENCE OF A LIVE DT LINK, CREATE OR FIND A MATCHING FOLDER PATH IN DT
tell application id "DNtp"
	set oDTFolder to my GetParallelFolder(oProject)
	if pblnJustFolder then
		set strDTLink to reference URL of oDTFolder
	else
		-- CREATE OR FIND A NOTE FILE FOR THIS PROJECT
		set strNoteName to "• " & strProjName & " notes" & pstrOO5Suffix
		set recNotes to my GetNotes(oDTFolder, strNoteName, strProjID)
		set strDTLink to reference URL of recNotes
	end if
end tell

-- PLACE AN RTF-FORMATTED DT LINK TO THE NEW OR PRE-EXISTING NOTES IN THE NOTES FIELD OF THE PROJECT
if pblnJustFolder then
	set strLinkTitle to pstrFolderLinkTitle
else
	set strLinkTitle to pstrNoteLinkTitle
end if

(*
my PasteToNote(oProject, strLinkTitle, strDTLink)

*)

-- AND FOLLOW THE LINK TO THE FOLDER OR OO5 DOCUMENT IN DT2
FollowDTLink(strDTLink)
tell application id "DNtp" to activate

end run

– Return the first project selected in the Omnifocus GUI
on GetSeldProject()
tell application id “com.omnigroup.OmniFocus2”
tell front document
– GET THE FIRST SELECTED PROJECT (CONTENT OR SIDEBAR)
if (count of document windows) < 1 then return {missing value, “”, “”}
tell front document window
set oProject to missing value
repeat with oPanel in {content, sidebar}
set lstSelns to (value of selected trees of oPanel where (class of value = task) or (class of value = project))
if (count of lstSelns) > 0 then
set oProject to first item of lstSelns
exit repeat
end if
end repeat
if oProject is missing value then return {missing value, “”, “”}
if class of oProject = task then set oProject to containing project of oProject
tell oProject to return {it, name, id}
end tell
end tell
end tell
end GetSeldProject

on DTLinksInNote(oProject)
tell application id “com.omnigroup.OmniFocus2”
– DOES ITS NOTE CONTAIN A DEVONTHINK URL ?
set strQuery to “select CAST(notexmldata as text) from task where persistentIdentifier = “” & (id of oProject) & “””
set strNoteXML to do shell script “sqlite3 " & pstrDBPath & space & quoted form of strQuery
set strLink to pstrXMLPrefix & pstrDTPrefix
set blnOpened to false
if strNoteXML contains strLink then
set {strDlm, my text item delimiters} to {my text item delimiters, pstrRunDelim}
set lstRuns to text items of strNoteXML
set my text item delimiters to “”
set {blnFolder, blnNote} to {false, false}
set {strFolderURL, strNotesURL} to {”", “”}

		set strStart to text 1 thru 2 of pstrNoteLinkTitle
		repeat with oRun in lstRuns
			set lstSections to text items of oRun
			if length of lstSections > 1 then
				set strLabel to item -1 of lstSections
				set strPreamble to item -2 of lstSections
				
				if strLabel begins with strStart then
					if not blnFolder then
						if strLabel contains "folder" then
							set strFolderURL to my ParseLink(strPreamble)
							if strFolderURL ≠ "" then set blnFolder to true
						end if
					end if
					if not blnNote then
						if strLabel contains "notes" then
							set strNotesURL to my ParseLink(strPreamble)
							if strNotesURL ≠ "" then set blnNotes to true
						end if
					end if
				end if
				if blnFolder and blnNote then exit repeat
			end if
		end repeat
		
		set my text item delimiters to strDlm
		return {strFolderURL, strNotesURL}
	else
		if note of oProject = "" then set note of oProject to space -- (seems to prepare note for easier opening later)
		return {"", ""}
	end if
end tell

end DTLinksInNote

on ParseLink(strXML)
set {strDlm, my text item delimiters} to {my text item delimiters, pstrXMLPrefix & pstrDTPrefix}
set lstParts to text items of strXML
set strURL to “”
if length of lstParts > 1 then
set my text item delimiters to “”
set strURL to first text item of item 2 of lstParts
if length of strURL = plngURLchars then
set strURL to pstrDTPrefix & strURL
else
set strURL to “”
end if
end if
set my text item delimiters to strDlm
return strURL
end ParseLink

on FollowDTLink(strURL)
if strURL ≠ “” then
set blnOpened to (do shell script "open " & quoted form of strURL) = “”
if blnOpened then
tell application id “DNtp” to activate
return true
else
return false
end if
else
return false
end if
end FollowDTLink

on GetParallelFolder(oProject)
set {strPath, strFolderDB, strProject} to GetProjPath(oProject)
tell application id “DNtp”

	-- CHOOSE THE TARGET DATABASE
	-- EITHER FROM A PATH IN THE ENCLOSING FOLDER, OR FROM THE DEFAULT
	if strFolderDB ≠ "" then
		set strDb to strFolderDB
	else
		set strDb to (POSIX path of pstrDocsPath) & pstrDTDB & pstrDTsuffix
	end if
	
	set oDb to open database strDb
	if oDb is missing value then
		set oAnswer to display dialog "Create new Devonthink database at \"" & strDb & "\" ?" buttons {"Cancel", "OK"} default button 1
		if the button returned of oAnswer is "Cancel" then return
		set oDb to create database strDb
	end if
	
	-- DEPENDING ON GLOBAL SETTING, OPTIONALLY CREATE NEW FOLDER WITHIN SYNC GROUP
	if pblnUseSyncAsRoot then
		set oLocn to create location pstrSync & strPath in oDb
	else
		set oLocn to create location strPath in oDb
	end if
	set URL of oLocn to pstrOFPrefix & (id of oProject)
	return oLocn
end tell

end GetParallelFolder

on GetProjPath(oProject)
tell application id “com.omnigroup.OmniFocus2”
set strProject to name of oProject
set strPath to strProject
set oContainer to container of oProject

	set blnFolderFound to false
	set strFolderDB to ""
	
	set cClass to the class of oContainer
	repeat while cClass is not document
		if not blnFolderFound then
			if cClass is folder then
				set strFolderDB to note of oContainer
				if ((strFolderDB ends with pstrDTsuffix) and my FileExists(strFolderDB)) then set blnFolderFound to true
			end if
		end if
		set strPath to name of oContainer & "/" & strPath
		set oContainer to container of oContainer
		set cClass to the class of oContainer
	end repeat
	return {"/" & strPath, strFolderDB, strProject}
end tell

end GetProjPath

on GetNotes(oDTFolder, strNoteFile, strProjectID)
tell application id “DNtp”
set lstNoteRecs to children of oDTFolder where name = strNoteFile
if length of lstNoteRecs > 0 then
return first item of lstNoteRecs
else
– IMPORT A FRESH TEMPLATE FILE FROM THE SAME FOLDER AS THIS SCRIPT
tell application id “MACS” to set oScriptFolder to container of file (path to me)
set strScriptFolder to POSIX path of (oScriptFolder as alias)
set strTemplate to strScriptFolder & pstrOO5Template & pstrOO5Suffix
if my BundleExists(strTemplate) or my FileExists(strTemplate) then
set oRec to import strTemplate to oDTFolder
else
– OR FROM INSIDE THE SCRIPT BUNDLE
set strTemplate to POSIX path of (path to me) & pstrOO5Template & pstrOO5Suffix
try
set oRec to import strTemplate to oDTFolder
on error
display alert pstrOOTemplate & " not found in this script bundle"
return missing value
end try
end if

		tell oRec
			if it is missing value then return it
			set its name to strNoteFile
			set its URL to pstrOFPrefix & strProjectID
		end tell
		return oRec
	end if
end tell

end GetNotes

on FileExists(strPath)
(do shell script ("test -e " & quoted form of strPath & “; echo $?”)) = “0”
end FileExists

on BundleExists(strPath)
(do shell script ("test -d " & quoted form of strPath & “; echo $?”)) = “0”
end BundleExists[/code]


#6

@TheWart, I modified this script. Now, it creates a link back to DTPO in the OF2 project note. I hope this solves the issue.

Code:

property pblnJustFolder : true
property pblnUseSyncAsRoot : false
-- Robin Trew 
-- ver .204 June 19 2011
--	Aug 22 2011
-- Ver .208 Still defaults to the DT Database named in pstrDTDB (below)
--			but can use different DT databases for different OF folders
--			(will use the first existing DT database whose POSIX path is found in the note of an enclosing OmniFocus folder
--			[a script for editing OF folder notes can be found at 
--			http://forums.omnigroup.com/showthread.php?t=21942]
-- ver .211 Sep 12 2011
--			Adds option, above - top of script, to make the Sync group the root of all new folders
-- Disclaimer
-- This is just a rough draft of something which I have sketched for my own personal use, 
-- and which is provided purely as an illustration of possible approaches to coding.
-- You are free to adapt and reuse any part of it, without any warranties, implied
-- or explicit, as to its behaviour or suitability for use
-- IF property pblnJustFolder : **false**  (see top of script)
-- CREATES/OPENS A PROJECT NOTES FILE (STORED IN DEVONthink 2) FOR THE SELECTED OMNIFOCUS PROJECT.
-- 1.	The project notes file is an OmniOutliner 5 document stored in DEVONthink 2 folder
-- 2.	The Devonthink record contains a hyperlink back to the OmniFocus project
-- 2.	The script ensures that a DT hyperlink to the document in DevonThink is placed in the note field of the project
-- 3.	In the absence of a link to existing notes in DevonThink, 
--		the script will seek or create the OO5 file in a DevonThink folder which matches 
--		the folder path of the project in OmniFocus 
--		(and add the hyperlinks from project to notes, and from folder and notes to project)
-- OR - IF property pblnJustFolder : **true** (see top of script)
-- JUST CREATES/OPENS A PROJECT MATERIALS FOLDER (IN DEVONthink 2) 
-- FOR THE SELECTED OMNIFOCUS PROJECT.
-- 1.	The script ensures that a DT hyperlink to the document in DevonThink is placed in 
--		the note field of the project
-- 2.	In the absence of a link to an existing folder in DevonThink, 
--		the script will add the hyperlinks from project to folder, and from folder to project)
-- Acknowledgements
-- Inspired by Jim Harrison's excellent scripts, which use the Finder rather than DEVONthink 2
-- http://jhh.med.virginia.edu/main/OmniFocusScripts
-- The icon attached to this file is from the Float collection by Corey Marion
-- http://iconfactory.com/freeware/preview/flot
-- GLOBAL CONSTANTS
-- Initially assumes that project folders will be maintained in 
-- a database named [UserName]/Documents/Omnifocus Notes.
-- Edit the name and path below to change the location and/or name 
--of the main Projects folder that will contain the individual project folders
property pstrDTDB : "OmniFocus Notes" -- name of default Devonthink Database
property pstrDTsuffix : ".dtBase2"
property pstrDocsPath : (path to documents folder as string) -- path to ~/Documents
property pstrSync : "/Sync"
property pstrOO5Template : "Default"
property pstrOO5Suffix : ".ooutline"
property pstrOFPrefix : "omnifocus:///task/"
property pstrXMLPrefix : "<value key=\"link\">"
property pstrRunDelim : "</lit></run>"
property pstrDTPrefix : "x-devonthink-item://"
property pstrDBPath : "$HOME/Library/Containers/com.omnigroup.OmniFocus2/Data/Library/Caches/com.omnigroup.OmniFocus2/OmniFocusDatabase2"
--property pstrDBPath : "/Users/detach/Library/Containers/com.omnigroup.OmniFocus2.MacAppStore/Data/Library/Caches/com.omnigroup.OmniFocus2.MacAppStore/OmniFocusDatabase2"
property pstrFolderLinkTitle : "[Devonthink folder for this project]"
property pstrNoteLinkTitle : "[Devonthink ooutline notes]"
property plngURLchars : 36
on run
	-- IS A PROJECT (OR ONE OF ITS TASKS) SELECTED IN OMNIFOCUS ?
	set {oProject, strProjName, strProjID} to GetSeldProject()
	if oProject is missing value then return
	-- IS THERE A RELEVANT LINK IN THE NOTE FIELD OF THE PROJECT ?
	set {strFolderURL, stroo5URL} to DTLinksInNote(oProject)
	-- AND IF SO, DOES IT LEAD ANYWHERE ?
	if pblnJustFolder then
		if strFolderURL ≠ "" then if FollowDTLink(strFolderURL) then return
	else
		if stroo5URL ≠ "" then if FollowDTLink(stroo5URL) then return
	end if
	-- IN THE ABSENCE OF A LIVE DT LINK, CREATE OR FIND A MATCHING FOLDER PATH IN DT
	tell application id "DNtp"
		set oDTFolder to my GetParallelFolder(oProject)
		if pblnJustFolder then
			set strDTLink to reference URL of oDTFolder
		else
			-- CREATE OR FIND A NOTE FILE FOR THIS PROJECT
			set strNoteName to "• " & strProjName & " notes" & pstrOO5Suffix
			set recNotes to my GetNotes(oDTFolder, strNoteName, strProjID)
			set strDTLink to reference URL of recNotes
		end if
	end tell
	-- DT REFERENCE URL TO OF PROJECT NOTE: unlocked2412
	tell application "OmniFocus"
		tell front document
			set note of oProject to strDTLink
		end tell
	end tell
	-- PLACE AN RTF-FORMATTED DT LINK TO THE NEW OR PRE-EXISTING NOTES IN THE NOTES FIELD OF THE PROJECT
	if pblnJustFolder then
		set strLinkTitle to pstrFolderLinkTitle
	else
		set strLinkTitle to pstrNoteLinkTitle
	end if
	(*
	my PasteToNote(oProject, strLinkTitle, strDTLink)
*)
	-- AND FOLLOW THE LINK TO THE FOLDER OR OO5 DOCUMENT IN DT2
	FollowDTLink(strDTLink)
	tell application id "DNtp" to activate
end run
-- Return the first project selected in the Omnifocus GUI
on GetSeldProject()
	tell application "OmniFocus"
		tell front document
			-- GET THE FIRST SELECTED PROJECT (CONTENT OR SIDEBAR)
			if (count of document windows) < 1 then return {missing value, "", ""}
			tell front document window
				set oProject to missing value
				repeat with oPanel in {content, sidebar}
					set lstSelns to (value of selected trees of oPanel where (class of value = task) or (class of value = project))
					if (count of lstSelns) > 0 then
						set oProject to first item of lstSelns
						exit repeat
					end if
				end repeat
				if oProject is missing value then return {missing value, "", ""}
				if class of oProject = task then set oProject to containing project of oProject
				tell oProject to return {it, name, id}
			end tell
		end tell
	end tell
end GetSeldProject
on DTLinksInNote(oProject)
	tell application "OmniFocus"
		-- DOES ITS NOTE CONTAIN A DEVONTHINK URL ?
		set strQuery to "select CAST(notexmldata as text) from task where persistentIdentifier = \"" & (id of oProject) & "\""
		set strNoteXML to do shell script "sqlite3 " & pstrDBPath & space & quoted form of strQuery
		set strLink to pstrXMLPrefix & pstrDTPrefix
		set blnOpened to false
		if strNoteXML contains strLink then
			set {strDlm, my text item delimiters} to {my text item delimiters, pstrRunDelim}
			set lstRuns to text items of strNoteXML
			set my text item delimiters to "<lit>"
			set {blnFolder, blnNote} to {false, false}
			set {strFolderURL, strNotesURL} to {"", ""}
			set strStart to text 1 thru 2 of pstrNoteLinkTitle
			repeat with oRun in lstRuns
				set lstSections to text items of oRun
				if length of lstSections > 1 then
					set strLabel to item -1 of lstSections
					set strPreamble to item -2 of lstSections
					if strLabel begins with strStart then
						if not blnFolder then
							if strLabel contains "folder" then
								set strFolderURL to my ParseLink(strPreamble)
								if strFolderURL ≠ "" then set blnFolder to true
							end if
						end if
						if not blnNote then
							if strLabel contains "notes" then
								set strNotesURL to my ParseLink(strPreamble)
								if strNotesURL ≠ "" then set blnNotes to true
							end if
						end if
					end if
					if blnFolder and blnNote then exit repeat
				end if
			end repeat
			set my text item delimiters to strDlm
			return {strFolderURL, strNotesURL}
		else
			if note of oProject = "" then set note of oProject to space -- (seems to prepare note for easier opening later)
			return {"", ""}
		end if
	end tell
end DTLinksInNote
on ParseLink(strXML)
	set {strDlm, my text item delimiters} to {my text item delimiters, pstrXMLPrefix & pstrDTPrefix}
	set lstParts to text items of strXML
	set strURL to ""
	if length of lstParts > 1 then
		set my text item delimiters to "</value>"
		set strURL to first text item of item 2 of lstParts
		if length of strURL = plngURLchars then
			set strURL to pstrDTPrefix & strURL
		else
			set strURL to ""
		end if
	end if
	set my text item delimiters to strDlm
	return strURL
end ParseLink
on FollowDTLink(strURL)
	if strURL ≠ "" then
		set blnOpened to (do shell script "open " & quoted form of strURL) = ""
		if blnOpened then
			tell application id "DNtp" to activate
			return true
		else
			return false
		end if
	else
		return false
	end if
end FollowDTLink
on GetParallelFolder(oProject)
	set {strPath, strFolderDB, strProject} to GetProjPath(oProject)
	tell application id "DNtp"
		-- CHOOSE THE TARGET DATABASE
		-- EITHER FROM A PATH IN THE ENCLOSING FOLDER, OR FROM THE DEFAULT
		if strFolderDB ≠ "" then
			set strDb to strFolderDB
		else
			set strDb to (POSIX path of pstrDocsPath) & pstrDTDB & pstrDTsuffix
		end if
		set oDb to open database strDb
		if oDb is missing value then
			set oAnswer to display dialog "Create new Devonthink database at \"" & strDb & "\" ?" buttons {"Cancel", "OK"} default button 1
			if the button returned of oAnswer is "Cancel" then return
			set oDb to create database strDb
		end if
		-- DEPENDING ON GLOBAL SETTING, OPTIONALLY CREATE NEW FOLDER WITHIN SYNC GROUP
		if pblnUseSyncAsRoot then
			set oLocn to create location pstrSync & strPath in oDb
		else
			set oLocn to create location strPath in oDb
		end if
		set URL of oLocn to pstrOFPrefix & (id of oProject)
		return oLocn
	end tell
end GetParallelFolder
on GetProjPath(oProject)
	tell application "OmniFocus"
		set strProject to name of oProject
		set strPath to strProject
		set oContainer to container of oProject
		set blnFolderFound to false
		set strFolderDB to ""
		set cClass to the class of oContainer
		repeat while cClass is not document
			if not blnFolderFound then
				if cClass is folder then
					set strFolderDB to note of oContainer
					if ((strFolderDB ends with pstrDTsuffix) and my FileExists(strFolderDB)) then set blnFolderFound to true
				end if
			end if
			set strPath to name of oContainer & "/" & strPath
			set oContainer to container of oContainer
			set cClass to the class of oContainer
		end repeat
		return {"/" & strPath, strFolderDB, strProject}
	end tell
end GetProjPath
on GetNotes(oDTFolder, strNoteFile, strProjectID)
	tell application id "DNtp"
		set lstNoteRecs to children of oDTFolder where name = strNoteFile
		if length of lstNoteRecs > 0 then
			return first item of lstNoteRecs
		else
			-- IMPORT A FRESH TEMPLATE FILE FROM THE SAME FOLDER AS THIS SCRIPT
			tell application id "MACS" to set oScriptFolder to container of file (path to me)
			set strScriptFolder to POSIX path of (oScriptFolder as alias)
			set strTemplate to strScriptFolder & pstrOO5Template & pstrOO5Suffix
			if my BundleExists(strTemplate) or my FileExists(strTemplate) then
				set oRec to import strTemplate to oDTFolder
			else
				-- OR FROM INSIDE THE SCRIPT BUNDLE
				set strTemplate to POSIX path of (path to me) & pstrOO5Template & pstrOO5Suffix
				try
					set oRec to import strTemplate to oDTFolder
				on error
					display alert pstrOOTemplate & " not found in this script bundle"
					return missing value
				end try
			end if
			tell oRec
				if it is missing value then return it
				set its name to strNoteFile
				set its URL to pstrOFPrefix & strProjectID
			end tell
			return oRec
		end if
	end tell
end GetNotes
on FileExists(strPath)
	(do shell script ("test -e " & quoted form of strPath & "; echo $?")) = "0"
end FileExists
on BundleExists(strPath)
	(do shell script ("test -d " & quoted form of strPath & "; echo $?")) = "0"
end BundleExists

#7

You are great, @unlocked2412. Thank you so much!

Really pushing my luck…but can you figure out why the sister script of Rob’s can only work when the file extension is .oo3? I tried to change it to the new .ooutline extension but the script throws an error at making a link to the new file.

I also tried to use the fix you provided above to paste the OmniOutliner document link back into the OmniFocus project notes, but must have placed it in the wrong place.

property pblnJustFolder : false
property pblnUseSyncAsRoot : false
property pblnPreferFTToOmniOO3 : false

-- Robin Trew

-- ver .204 June 19 2011
-- ver .205  [corrects a bug in which a newly created folder was not immediately displayed]
-- ver .206	[unifies the folder and notes scripts, differing only by value of
--			the property pblnJustFolder at the top of the script]
--	Aug 22 2011
-- Ver .208 Still defaults to the DT Database named in pstrDTDB (below)
--			but can use different DT databases for different OF folders
--			(will use the first existing DT database whose POSIX path is found in the note of an enclosing OmniFocus folder
--			[a script for editing OF folder notes can be found at 
--			http://forums.omnigroup.com/showthread.php?t=21942]
-- ver .211 Sep 12 2011
--			Adds option, above - top of script, to make the Sync group the root of all new folders
-- Ver .214 Nov 19 2012 Uses www.FoldingText.com rather than OO3 if pblnPreferFTToOmniOO3=true

-- Disclaimer
-- This is just a rough draft of something which I have sketched for my own personal use, 
-- and which is provided purely as an illustration of possible approaches to coding.
-- You are free to adapt and reuse any part of it, without any warranties, implied
-- or explicit, as to its behaviour or suitability for use

-- IF property pblnJustFolder : **false**  (see top of script)
-- CREATES/OPENS A PROJECT NOTES FILE (STORED IN DEVONthink 2) FOR THE SELECTED OMNIFOCUS PROJECT.
-- 1.	The project notes file is an OmniOutliner 3 document stored in DEVONthink 2 folder
-- 2.	The Devonthink record contains a hyperlink back to the OmniFocus project
-- 2.	The script ensures that a DT hyperlink to the document in DevonThink is placed in the note field of the project
-- 3.	In the absence of a link to existing notes in DevonThink, 
--		the script will seek or create the 003 file in a DevonThink folder which matches 
--		the folder path of the project in OmniFocus 
--		(and add the hyperlinks from project to notes, and from folder and notes to project)

-- OR - IF property pblnJustFolder : **true** (see top of script)
-- JUST CREATES/OPENS A PROJECT MATERIALS FOLDER (IN DEVONthink 2) 
-- FOR THE SELECTED OMNIFOCUS PROJECT.
-- 1.	The script ensures that a DT hyperlink to the document in DevonThink is placed in 
--		the note field of the project
-- 2.	In the absence of a link to an existing folder in DevonThink, 
--		the script will add the hyperlinks from project to folder, and from folder to project)


-- Acknowledgements
-- Inspired by Jim Harrison's excellent scripts, which use the Finder rather than DEVONthink 2
-- http://jhh.med.virginia.edu/main/OmniFocusScripts
-- The icon attached to this file is from the Float collection by Corey Marion
-- http://iconfactory.com/freeware/preview/flot

-- GLOBAL CONSTANTS
-- Initially assumes that project folders will be maintained in 
-- a database named [UserName]/Documents/Omnifocus Notes.
-- Edit the name and path below to change the location and/or name 
--of the main Projects folder that will contain the individual project folders

property pstrDTDB : "OmniFocus Notes" -- name of default Devonthink Database
property pstrDTsuffix : ".dtBase2"
property pstrDocsPath : (path to documents folder as string) -- path to ~/Documents
property pstrSync : "/Mobile Sync"

property pstrTemplate : "Default"
property pstrOO3Suffix : ".oo3"
property pstrFTSuffix : ".ft"

property pstrOFPrefix : "omnifocus:///task/"
property pstrXMLPrefix : "<value key=\"link\">"
property pstrRunDelim : "</lit></run>"
property pstrDTPrefix : "x-devonthink-item://"
property pstrDBPath : "$HOME/Library/Containers/com.omnigroup.OmniFocus2/Data/Library/Caches/com.omnigroup.OmniFocus2/OmniFocusDatabase2"
property pstrFolderLinkTitle : "[Devonthink folder for this project]"
property pstrNoteLinkTitle : "[Devonthink notes]"
property plngURLchars : 36

on run
	
	
	-- IS A PROJECT (OR ONE OF ITS TASKS) SELECTED IN OMNIFOCUS ?
	set {oProject, strProjName, strProjID} to GetSeldProject()
	if oProject is missing value then return
	
	-- IS THERE A RELEVANT LINK IN THE NOTE FIELD OF THE PROJECT ?
	set {strFolderURL, strNotesURL} to DTLinksInNote(oProject)
	
	-- AND IF SO, DOES IT LEAD ANYWHERE ?
	if pblnJustFolder then
		if strFolderURL ≠ "" then if FollowDTLink(strFolderURL) then return
	else
		if strNotesURL ≠ "" then if FollowDTLink(strNotesURL) then return
	end if
	
	-- IN THE ABSENCE OF A LIVE DT LINK, CREATE OR FIND A MATCHING FOLDER PATH IN DT
	tell application id "DNtp"
		set oDTFolder to my GetParallelFolder(oProject)
		if pblnJustFolder then
			set strDTLink to reference URL of oDTFolder
		else
			-- WHICH NOTE APP ARE WE USING - FOLDINGTEXT OR OO3 ?
			set blnFT to pblnPreferFTToOmniOO3 and my isAppInstalled("com.foldingtext.FoldingText")
			if blnFT then
				set strNoteSuffix to pstrFTSuffix
			else
				set strNoteSuffix to pstrOO3Suffix
			end if
			
			-- CREATE OR FIND A NOTE FILE FOR THIS PROJECT
			set strNoteName to "• " & strProjName & " notes" & strNoteSuffix
			set recNotes to my GetNotes(oDTFolder, strNoteName, strNoteSuffix, strProjID, blnFT)
			set strDTLink to reference URL of recNotes
		end if
	end tell
	
	-- PLACE AN RTF-FORMATTED DT LINK TO THE NEW OR PRE-EXISTING NOTES  IN THE NOTES FIELD OF THE PROJECT
	if pblnJustFolder then
		set strLinkTitle to pstrFolderLinkTitle
	else
		set strLinkTitle to pstrNoteLinkTitle
	end if
	(*
	my PasteToNote(oProject, strLinkTitle, strDTLink)
*)
	
	-- AND FOLLOW THE LINK TO THE FOLDER OR NOTES DOCUMENT IN DT2
	FollowDTLink(strDTLink)
	tell application id "DNtp" to activate
end run

-- Check whether an app is installed e.g. isAppInstalled("com.foldingtext.FoldingText")
-- for http://www.foldingtext.com
on isAppInstalled(strBundleCode)
	try
		tell application "Finder"
			name of (application file id strBundleCode) ≠ ""
		end tell
	on error
		return false
	end try
end isAppInstalled

-- Return the first project selected in the Omnifocus GUI
on GetSeldProject()
	tell application id "OFOC"
		tell front document
			-- GET THE FIRST SELECTED PROJECT (CONTENT OR SIDEBAR)
			if (count of document windows) < 1 then return {missing value, "", ""}
			tell front document window
				set oProject to missing value
				repeat with oPanel in {content, sidebar}
					set lstSelns to (value of selected trees of oPanel where (class of value = task) or (class of value = project))
					if (count of lstSelns) > 0 then
						set oProject to first item of lstSelns
						exit repeat
					end if
				end repeat
				if oProject is missing value then return {missing value, "", ""}
				if class of oProject = task then set oProject to containing project of oProject
				tell oProject to return {it, name, id}
			end tell
		end tell
	end tell
end GetSeldProject

on DTLinksInNote(oProject)
	tell application id "OFOC"
		-- DOES ITS NOTE CONTAIN A DEVONTHINK URL ?
		set strQuery to "select CAST(notexmldata as text) from task where persistentIdentifier = \"" & (id of oProject) & "\""
		set strNoteXML to do shell script "sqlite3 " & pstrDBPath & space & quoted form of strQuery
		set strLink to pstrXMLPrefix & pstrDTPrefix
		set blnOpened to false
		if strNoteXML contains strLink then
			set {strDlm, my text item delimiters} to {my text item delimiters, pstrRunDelim}
			set lstRuns to text items of strNoteXML
			set my text item delimiters to "<lit>"
			set {blnFolder, blnNote} to {false, false}
			set {strFolderURL, strNotesURL} to {"", ""}
			
			set strStart to text 1 thru 2 of pstrNoteLinkTitle
			repeat with oRun in lstRuns
				set lstSections to text items of oRun
				if length of lstSections > 1 then
					set strLabel to item -1 of lstSections
					set strPreamble to item -2 of lstSections
					
					if strLabel begins with strStart then
						if not blnFolder then
							if strLabel contains "folder" then
								set strFolderURL to my ParseLink(strPreamble)
								if strFolderURL ≠ "" then set blnFolder to true
							end if
						end if
						if not blnNote then
							if strLabel contains "notes" then
								set strNotesURL to my ParseLink(strPreamble)
								if strNotesURL ≠ "" then set blnNotes to true
							end if
						end if
					end if
					if blnFolder and blnNote then exit repeat
				end if
			end repeat
			
			set my text item delimiters to strDlm
			return {strFolderURL, strNotesURL}
		else
			if note of oProject = "" then set note of oProject to space -- (seems to prepare note for easier opening later)
			return {"", ""}
		end if
	end tell
end DTLinksInNote

on ParseLink(strXML)
	set {strDlm, my text item delimiters} to {my text item delimiters, pstrXMLPrefix & pstrDTPrefix}
	set lstParts to text items of strXML
	set strURL to ""
	if length of lstParts > 1 then
		set my text item delimiters to "</value>"
		set strURL to first text item of item 2 of lstParts
		if length of strURL = plngURLchars then
			set strURL to pstrDTPrefix & strURL
		else
			set strURL to ""
		end if
	end if
	set my text item delimiters to strDlm
	return strURL
end ParseLink

on FollowDTLink(strURL)
	if strURL ≠ "" then
		set blnOpened to (do shell script "open " & quoted form of strURL) = ""
		if blnOpened then
			tell application id "DNtp" to activate
			return true
		else
			return false
		end if
	else
		return false
	end if
end FollowDTLink


on GetParallelFolder(oProject)
	set {strPath, strFolderDB, strProject} to GetProjPath(oProject)
	tell application id "DNtp"
		
		-- CHOOSE THE TARGET DATABASE
		-- EITHER FROM A PATH IN THE ENCLOSING FOLDER, OR FROM THE DEFAULT
		if strFolderDB ≠ "" then
			set strDb to strFolderDB
		else
			set strDb to (POSIX path of pstrDocsPath) & pstrDTDB & pstrDTsuffix
		end if
		
		set oDb to open database strDb
		if oDb is missing value then
			set oAnswer to display dialog "Create new Devonthink database at \"" & strDb & "\" ?" buttons {"Cancel", "OK"} default button 1
			if the button returned of oAnswer is "Cancel" then return
			set oDb to create database strDb
		end if
		
		-- DEPENDING ON GLOBAL SETTING, OPTIONALLY CREATE NEW FOLDER WITHIN SYNC GROUP
		if pblnUseSyncAsRoot then
			set oLocn to create location pstrSync & strPath in oDb
		else
			set oLocn to create location strPath in oDb
		end if
		set URL of oLocn to pstrOFPrefix & (id of oProject)
		return oLocn
	end tell
end GetParallelFolder

on GetProjPath(oProject)
	tell application id "OFOC"
		set strProject to name of oProject
		set strPath to strProject
		set oContainer to container of oProject
		
		set blnFolderFound to false
		set strFolderDB to ""
		
		set cClass to the class of oContainer
		repeat while cClass is not document
			if not blnFolderFound then
				if cClass is folder then
					set strFolderDB to note of oContainer
					if ((strFolderDB ends with pstrDTsuffix) and my FileExists(strFolderDB)) then set blnFolderFound to true
				end if
			end if
			set strPath to name of oContainer & "/" & strPath
			set oContainer to container of oContainer
			set cClass to the class of oContainer
		end repeat
		return {"/" & strPath, strFolderDB, strProject}
	end tell
end GetProjPath

on GetNotes(oDTFolder, strNoteFile, strNoteSuffix, strProjectID)
	tell application id "DNtp"
		set lstNoteRecs to children of oDTFolder where name = strNoteFile
		if length of lstNoteRecs > 0 then
			return first item of lstNoteRecs
		else
			-- IMPORT A FRESH TEMPLATE FILE FROM THE SAME FOLDER AS THIS SCRIPT
			set strScriptFolder to POSIX path of (path to me)
			--tell application id "MACS" to set oScriptFolder to container of oThisScript
			
			set strTemplate to strScriptFolder & pstrTemplate & strNoteSuffix
			if my BundleExists(strTemplate) or my FileExists(strTemplate) then
				set oRec to import strTemplate to oDTFolder
			else
				-- OR FROM INSIDE THE SCRIPT BUNDLE
				set strTemplate to POSIX path of (path to me) & pstrTemplate & strNoteSuffix
				try
					set oRec to import strTemplate to oDTFolder
				on error
					display alert strTemplate & " not found in this script bundle"
					return missing value
				end try
			end if
			
			tell oRec
				if it is missing value then return it
				set its name to strNoteFile
				set its URL to pstrOFPrefix & strProjectID
			end tell
			return oRec
		end if
	end tell
end GetNotes

on FileExists(strPath)
	(do shell script ("test -e " & quoted form of strPath & "; echo $?")) = "0"
end FileExists

on BundleExists(strPath)
	(do shell script ("test -d " & quoted form of strPath & "; echo $?")) = "0"
end BundleExists


(*
on PasteToNote(oObj, strLegend, strURL)
	set strHTML to quoted form of ("<font face=\"Helvetica\"><a href=\"" & strURL & "\">" & strLegend & "</a></font><p>")
	do shell script "echo " & strHTML & "  | textutil -format html -convert rtf -stdin -stdout | pbcopy -Prefer rtf"
	tell application id "OFOC"
		tell default document
			set lngWins to count of document windows
			if lngWins < 1 then make new document window
			set visible of front document window to true
			set oWin to front document window
			set cClass to class of oObj
			if not (cClass is in {task, inbox task, project}) then return
			tell oWin
				if «class FCvm» ≠ "project" then set «class FCvm» to "project"
				if cClass is inbox task then
					tell tree 1 of sidebar -- Inbox
						if not selected then set selected to true
					end tell
				else
					if (focus as list) ≠ {} then set focus to {}
					tell tree 2 of sidebar -- Library
						if not selected then set selected to true
					end tell
				end if
			end tell
			
			set strID to (id of oObj) as string -- haven't quite worked out why re-referencing the object
			if cClass = inbox task then
				set oObj to inbox task id strID -- seems to be necessary here  (for selection to work)
			else if cClass = task then
				set oObj to task id strID
			else
				set oObj to project id strID
			end if
			tell content of oWin
				«event OTREisal» {oObj}
				set oSeln to first selected tree
				set selected of oSeln to false
				set selected of oSeln to true
				set note expanded of oSeln to false
			end tell
		end tell
		activate
	end tell
	tell application id "sevs"
		keystroke "'" using {command down} -- second time if needed
		keystroke "v" using {command down} -- Paste
		-- keystroke "'" using {command down} -- second time if needed
	end tell
	do shell script "sleep 0.2"
	tell application id "OFOC"
		tell front document
			tell content of front document window
				set oSeln to (first selected tree)
				set selected of oSeln to false
				set note expanded of oSeln to false
				set selected of oSeln to true
			end tell
		end tell
	end tell
end PasteToNote
*)

#8

You’re welcome, @TheWart. I’m glad it worked for you.

Where did you got this script? Apparently, it asks for a template file in the resources folder of the script bundle.


#9

I got it through my link in the 5th post of this thread above. That makes sense since the script has a .scptd extension, and I can “View package contents” and locate the original .oo3 file the script is calling for.


#10

Fantastic. I forked Rob’s repository and made a modification to my branch.
Now, it creates .ooutline files and adds a link back to the Omnioutliner file in the OF2 project notes.
I hope this solves the problem.

OpenProjNOTESInDevn216.scptd.zip


#11

That definitely creates a link back and forth with the OO file, mirroring my OF nested project structure.

However, since it isn’t my default template I tried to copy mine into the .scptd file using "Show package package contents and get the following error:


#12

I think you’re copying a .otemplate file into the package contents. Can you try with a .ooutline file?


#13

That was it! OO file is now created and linked properly between both DTPO and OF.

For a final step, I may try to hack a way to have the DTPO link for the notes file appended to the respective OF project’s note field. After running the script, I would like to see in the OF project notes field:

[DEVONthink group for this project] x-devonthink-item://xxxxxxxx
[blank line]
[OmniOutliner notes for this project] x-devonthink-item://xxxxxxxx

Or, even better:

[DEVONthink group for this project] <-------- as a hyperlink to x-devonthink-item://xxxxxxxx
[blank line]
[OmniOutliner notes for this project] <-------- as a hyperlink to x-devonthink-item://xxxxxxxx

I tried to follow Rob’s original parsing of the hyperlink (which worked brilliantly in OF1) but couldn’t figure out how to paste rich text into the notes field of OF2.


#14

I’m glad it worked.
I made the improvement. Committed changes into my branch. Now it creates rich text links in OF Project Notes.
Is this what you had in mind?

OpenProjNOTESInDevn218.scptd.zip


#15

I am so grateful for your work, @unlocked2412. With the help of Keyboard Maestro, I am back to the functionality I enjoyed with Rob’s original scripts using OmniFocus 1.

For posterity and future development, here’s the final script I use, the Keyboard Maestro macros that connect it all, and a (very homespun) video that shows how I use them all together.

  1. AppleScript bundle improved by unlocked2412 with minor tweaks for my system.
  2. Keyboard Maestro macros shown in the video.
  3. Video of how I use the scripts and macros on my system with OmniFocus Pro v2, DEVONthink Pro Office v2, and OmniOutliner Pro v5.

#16

You’re welcome, @TheWart!
You explain really well, so it’s easy to understand what you are trying to do.
One suggestion to your excellent workflow: would you like to open both links with separate shortcuts via Applescript? If you move the window, the KM macro cannot locate the links, I think.
P.S.: I’m intrigued: what the purpose of Script: interactive Inbox?


#17

You’re kind, but things were easy to understand because you made the program do exactly what was needed!

I’d like to learn how to get OF to open either link with AppleScript, and the script would reach a wider audience than a Keyboard Maestro macro. Would it be possible to activate an AppleScript that, when activated from within the tasks of a project, throws a pop-up that allows me to choose between hyperlinks in the host project’s note field?

I’ve tested the KM macro in different window locations because the first mouse click is not dependent upon position of the OF window, but actually looks for the image of hyperlinked text on a white background. This is KM’s secret sauce for me: “Click at found image” does what no other shortcut program can.


#18

You’re right. It’s a clever solution but it is little faster and more reliable with Applescript.
I used “Click at found image” many times. In some cases, it’s the only option.
Yes, it’s possible. This is a starting point. To open DT link:

  • Store in a variable
    • The selected project or
  • The project of the selected task
  • Store in a variable the value of the attribute “link” of the style of the note of the project
  • Open variable contents via system events

Code:

--unlocked2412

tell application "OmniFocus"
	tell front document
		tell front document window
			set lst_values to (value of selected trees of content) whose (class of value = task) or (class of value = project)
			if (count of lst_values) = 0 then
				set lst_values to (value of selected trees of sidebar) whose (class of value = task) or (class of value = project)
				if (count of lst_values) = 0 then
					display notification "Select a project or task"
					return
				end if
			end if
			set the_value to item 1 of lst_values
			if class of the_value = task then
				set the_project to containing project of the_value
			else
				set the_project to the_value
			end if
		end tell
		set note_ref to a reference to (note of the_project)
		tell note_ref
			set dt_link to value of attribute "link" of style of paragraph 1
		end tell
	end tell
end tell

tell application "System Events"
	open location dt_link
end tell

#19

If you need to open OmniOutliner link, you must get de value of attribute “link” of style of paragraph 3.
I leave the code for reference:

Code
tell application "OmniFocus"
	tell front document
		tell front document window
			set lst_values to (value of selected trees of content) whose (class of value = task) or (class of value = project)
			if (count of lst_values) = 0 then
				set lst_values to (value of selected trees of sidebar) whose (class of value = task) or (class of value = project)
				if (count of lst_values) = 0 then
					display notification "Select a project or task"
					return
				end if
			end if
			set the_value to item 1 of lst_values
			if class of the_value = task then
				set the_project to containing project of the_value
			else
				set the_project to the_value
			end if
		end tell
		set note_ref to a reference to (note of the_project)
		tell note_ref
			set oo_link to value of attribute "link" of style of paragraph 3
		end tell
	end tell
end tell

tell application "System Events"
	open location oo_link
end tell

We can do that with a Standard Additions command (Applescript): choose from list.

Or, trigger a Keyboard Maestro palette. What do you prefer?
Tell me if you need some help, @TheWart.


#20

That is very helpful. I see how changing paragraph link to 1 allows the script to open the associated DTPO group. I’ve made two copies of the AppleScript and placed them in my OF toolbar. I’ll load both scripts into my existing Keyboard Maestro palette instead of my Rube Goldberg macros.

Thank you very much!

P.S. You asked earlier about the “Interactive Inbox” script in my toolbar. It reads my OF inbox and prompts for a project and a context to assign each inbox task. It was taken from the work of someone else in the forum. I’ll look for it and link it back here.

use AppleScript version "2.4" -- Yosemite (10.10) or later
    use scripting additions
    use O : script "omnifocus"
    property assign_method : 3

    if assign_method is 1 then
    	assign_project()
    else if assign_method is 2 then
    	assign_context()
    else if assign_method is 3 then
    	assign_project()
    	assign_context()
    end if


    on assign_project()
    	tell application "OmniFocus"
    		tell default document
    			repeat with _task in ((every inbox task) whose (completed) is false)
    				try
    					set nofolder_Projects to (name of (flattened projects where its folder is missing value and its status is active))
    					set folder_Projects to (name of (flattened projects where hidden of its folder is false and its status is active))
    					set projectNames to nofolder_Projects & folder_Projects
    					set _project to O's findProject(choose from list projectNames with title "Interactive Inbox" with prompt "Assign project to: " & name of _task without empty selection allowed)
    					set assigned container of _task to _project
    				on error
    					display notification "No project selected for " & name of _task & "."
    				end try
    			end repeat
    			
    			compact
    			
    			if (count of (inbox tasks whose (completed) is false)) > 1 then
    				display notification (((count of (inbox tasks whose (completed) is false)) as text) & " tasks remain in the inbox.")
    			else if (count of inbox tasks) > 0 then
    				display notification (((count of (inbox tasks whose (completed) is false)) as text) & " task remains in the inbox.")
    			end if
    		end tell
    	end tell
    end assign_project

    on assign_context()
    	tell application "OmniFocus"
    		tell default document
    			repeat with _task in ((every inbox task) whose (completed) is false)
    				try
    					set contextNames to (name of every flattened context whose hidden is false)
    					set _context to O's findContext(choose from list contextNames with title "Interactive Inbox" with prompt "Assign context to: " & name of _task without empty selection allowed)
    					set context of _task to _context
    				on error
    					display notification "No context selected for " & name of _task & "."
    				end try
    			end repeat
    			
    			compact
    			
    			if (count of (inbox tasks whose (completed) is false)) > 1 then
    				display notification (((count of (inbox tasks whose (completed) is false)) as text) & " tasks remain in the inbox.")
    			else if (count of inbox tasks) > 0 then
    				display notification (((count of (inbox tasks whose (completed) is false)) as text) & " task remains in the inbox.")
    			end if
    		end tell
    	end tell
    end assign_context

Display single action from inbox at a time