Integrating Omnifocus 2 with Devonthink Pro (Rob Trew scripts)

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.

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.

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

2 Likes

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:

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

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.

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

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.
3 Likes

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?

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.

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

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.

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
2 Likes

This is very promising and it would be great if I can make this working on my machine, however I get the following error message:

error “OmniFocus kreeg een fout: Error: near “Support”: syntax error” number 1

It is on this command line probably:

set strNoteXML to do shell script "sqlite3 " & pstrDBPath & space & quoted form of strQuery

I use this DBPath:

property pstrDBPath : "~/Library/Containers/com.omnigroup.OmniFocus2.MacAppStore/Data/Library/Application Support/Omnifocus/OmniFocus.ofocus"

Could you give me a hint where to look for a solution!

I’ll try to give you a hint. Did you bought OmniFocus in the Mac App Store?
If so, try with this line:
property pstrDBPath : "~/Library/Containers/com.omnigroup.OmniFocus2.MacAppStore/Data/Library/Caches/com.omnigroup.OmniFocus2.MacAppStore/OmniFocusDatabase2"

If not:

property pstrDBPath : "$HOME/Library/Containers/com.omnigroup.OmniFocus2/Data/Library/Caches/com.omnigroup.OmniFocus2/OmniFocusDatabase2"

I hope that helps. Let me know if it works.

2 Likes

Thank you very much for your quick reply and answer. It is great to have such supportive people, willing to help!

Your suggestions helped and everything is working now!

2 Likes

That’s fantastic! You’re welcome. I’m glad I could help.

I’m working through adding this to my system, but I’m having trouble trying to get KM to recognise the area of the OF note to activate a switch to either DevonThink or OmniOutliner.

It seems that KM just can’t ‘see’ the text image to be able to activate the click action.

I’ve tried taking my own screenshot and replacing the built-in as well as mucking around with fuzziness, but it’s not working.

Any ideas?

To respond to my own thread to help others; I discovered that I had to recreate the screenshots of the text in KM - I think my font must have been different.

I also went into System Preferences and toggled the reduce transparency setting to prevent colour bleed - although not sure this has any impact, to be honest.

Now, though, with my own screenshot, the text is recognised consistently and the script works.

I used the Rob Trew versions back in OF1 days, and it’s nice to have them back again!

If you like, I am leaving here two scripts I wrote in JXA to open DT link and OO link

Usage:

  • Select a project
  • Run script (or use a KM Execute Javascript Action)

P.S.: In one of the above posts, there is an AppleScript version.

(() => {
	// Data List JS ----------------------------------------------------------

	// bindMay (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
	const bindMay = (mb, mf) =>
		mb.nothing ? mb : mf(mb.just);

	// just :: a -> Just a
	const just = x => ({
		nothing: false,
		just: x
	});

	// nothing :: () -> Nothing
	const nothing = (optionalMsg) => ({
		nothing: true,
		msg: optionalMsg
	});

	// OF Functions -----------------------------------
	
	// projectMay :: Window -> Maybe Project
	const projectMay = win => {
		const seln = win.content.selectedTrees.value();
		return seln.length > 0 ?
			(Object.keys(seln[0].properties()).includes('reviewInterval')) ?
			just(seln[0]) : nothing('No projects selected') :
			nothing('No selection')
	}
	
	// linkMay :: OF Project -> URL String
	const linkMay = x => {
		try {
			return {
				nothing: false,
				just: x.note.paragraphs[0].style.attributes['link'].value()
			};
		} catch (e) {
			return {
				nothing: true,
				msg: 'No link in note'
			};
		}
	};

	// groupMay :: URL String -> maybe DT record
	const groupMay = x => {
		const strUUID = x.split('//')[1]
		return dt.getRecordWithUuid(strUUID) == null ?
			nothing('') :
			just(x)
	};

	// MAIN
	const
		ca = Application.currentApplication(),
		sa = (ca.includeStandardAdditions = true, ca),
		dt = Application('DEVONthink Pro'),
		of = Application('OmniFocus'),
		oDoc = of.defaultDocument,
		oWin = oDoc.documentWindows[0];
		
	const mb = bindMay(bindMay(projectMay(oWin), linkMay), groupMay)
	return (mb.nothing) ? mb.msg : ca.openLocation(mb.just)
})();
Open OO Link Code
(() => {
    // Data List JS ----------------------------------------------------------

    // bindMay (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
    const bindMay = (mb, mf) =>
        mb.nothing ? mb : mf(mb.just);

    // Data List JS ----------------------------------------------------------

    // just :: a -> Just a
    const just = x => ({
        nothing: false,
        just: x
    });

    // nothing :: () -> Nothing
    const nothing = (optionalMsg) => ({
        nothing: true,
        msg: optionalMsg
    });
    // OF Functions -----------------------------------
    
    // projectMay :: Window -> Maybe Project
    const projectMay = win => {
        const seln = win.content.selectedTrees.value();
        return seln.length > 0 ?
            (Object.keys(seln[0].properties()).includes('reviewInterval')) ?
            just(seln[0]) : nothing('No projects selected') :
            nothing('No selection')
    }
    
    // linkMay :: OF Project -> URL String
    const linkMay = x => {
        try {
            return {
                nothing: false,
                just: x.note.paragraphs[2].style.attributes['link'].value()
            };
        } catch (e) {
            return {
                nothing: true,
                msg: 'No link in note'
            };
        }
    };

    // groupMay :: URL String -> maybe DT record
    const groupMay = x => {
        const strUUID = x.split('//')[1]
        return dt.getRecordWithUuid(strUUID) == null ?
            nothing('') :
            just(x)
    };

    // MAIN
    const
        ca = Application.currentApplication(),
        sa = (ca.includeStandardAdditions = true, ca),
        dt = Application('DEVONthink Pro'),
        of = Application('OmniFocus'),
        oDoc = of.defaultDocument,
        oWin = oDoc.documentWindows[0];
        
    const mb = bindMay(bindMay(projectMay(oWin), linkMay), groupMay)
    return (mb.nothing) ? mb.msg : ca.openLocation(mb.just)
})();
2 Likes