I’m back! 
Before I jump in with my code, I want to be clear that DEVONthink is working wonderfully for me. I don’t know what took me so long to figure out what a great tool it is for my needs. My script is doing what I want, although there is always room for improvement. I am posting this to get feedback on whether I’m missing anything, overcomplicating things, etc.
Here’s my use case:
- I use a productivity method that is sort of a mashup between Agile Results and GTD.
- My tasks are grouped by focus areas such as family, personal, career, etc.
- I want to process all inboxes (email, paper, mind sweep, etc.) into OmniFocus with supporting material stored in DEVONthink.
- I want to be able to collect and process on my iPhone and iPad if necessary
- I’ve created automation that gets everything into OmniFocus and DEVONthink, but sometimes, I can’t assign to focus areas (folder) or projects when I do the processing step (usually this is because of limitations on iOS, sometimes I just end up moving things around later)
- The AppleScript I’ve created (code is below) is designed to be run after I process my OmniFocus inbox on my Mac. It finds all OmniFocus tasks with links to support materials in DEVONthink. It then checks if the task and support materials are in the appropriate folders and projects in both applications. If materials in DEVONthink aren’t in the right place, the script moves them.
- With the ability to keep every bit of supporting material in DEVONthink (I just love this!) and the consistent organization, I never lose track of anything.
- Eventually, I plan to add features to archive DEVONthink materials when the OmniFocus task is completed and probably other similar clean-up tasks as I think of them
With that, lengthy preamble. Here is my code. It works and DEVONthink works as expected. I also work as expected since I have a better idea of what I’m doing. 
I welcome any feedback on how to make it better.
(Mods: If this post is too off-topic from my original post, feel free to move it.)
property dtSupportDB : "~/DEVONthink/Support.dtBase2"
property white_space : {space, tab, return, (ASCII character 10), (ASCII character 13)}
property taskRecord : {ofFolderName:"", ofProjectName:"", ofTaskName:"", dtUUID:""}
property taskList : {}
on run
-- Get all OmniFocus tasks that have links to DEVONthink in the notes
set theOFTaskList to GetOFDTLinkedTasks()
-- Get count of OmniFocus tasks to loop through them all
set theOFTaskCount to count of theOFTaskList
-- If no OmniFocus tasks are found, exit
if theOFTaskCount is 0 then
return
end if
-- Loop through the OmniFocus tasks to check if associate DEVONthink record is
-- using the same folder and project hierarchy used in OmniFocus
repeat with theOFTask in theOFTaskList
-- Get the corresponding record from DEVONthink using the UUID
tell application id "DNtp"
-- Get the DEVONthink record referenced in the OmniFocus task
set dtRecord to get record with uuid dtUUID of theOFTask in dtSupportDB
-- If the DEVONthink record is missing, alert (will create logic to clean up OmniFocus later)
if dtRecord is missing value then
display dialog "DEVONthink item not found." & return & return & "OmniFocus Info" & return & return & "Project: " & ofProjectName of theOFTask & return & "Task: " & ofTaskName of theOFTask
else
-- Get the DEVONthink record name
set dtRecordName to name of dtRecord
-- Get the name of the group (aka project) the record is currently in
set dtProject to parent 1 of dtRecord
set dtProjectName to name of dtProject
-- If the DEVONthink record is in the global inbox, set the dtFolderName manually, otherwise, grab
-- the folder name from the record
-- (TBD - figure out why I can't handle this programmatically)
if dtProjectName is "Inbox" then
set dtFolderName to "Global Inbox"
else
-- Get the name of the top level group the record is in
set dtFolder to parent 1 of dtProject
set dtFolderName to name of dtFolder
end if
-- Get the name of the database the record is in
set dtDB to database of dtRecord
set dtDBName to name of dtDB
-- Check if the DEVONthink record is in the same folder and project that the OmniFocus task is in
-- and if it is in the Support database. If any of these conditions are not true, the DEVONthink
-- record needs to be moved
if (dtProjectName is not equal to ofProjectName of theOFTask) or (dtFolderName is not equal to ofFolderName of theOFTask) or (dtDBName is not equal to "Support") then
-- Get the ID of the destination group
set destProject to get record at dtSupportDB & "/" & ofFolderName of theOFTask & "/" & ofProjectName of theOFTask
-- Create the group if it doesn't exist already
if destProject is missing value then
set theDB to open database dtSupportDB
set destProject to create location ofFolderName of theOFTask & "/" & ofProjectName of theOFTask in theDB
end if
-- If record exists in same database, move with from clause
if dtDBName is equal to "Support" then
move record dtRecord to destProject from dtProject
else
move record dtRecord to destProject
end if
end if
end if
end tell
end repeat
end run
----------------------------------------------------------------------
-- Handler to extract DEVONthink UUID from reference URL
----------------------------------------------------------------------
on GetDTUUID(ofTaskNote)
set oldTIDs to AppleScript's text item delimiters
set AppleScript's text item delimiters to "://"
set ofNoteParsed to (every text item in ofTaskNote) as list
set dtUUID to item 2 of ofNoteParsed
set AppleScript's text item delimiters to oldTIDs
-- If the DEVONthink record is an email, decode the brackets in the UUID
set dtUUID to ReplaceChars(dtUUID, "%3C", "<")
set dtUUID to ReplaceChars(dtUUID, "%3E", ">")
end GetDTUUID
----------------------------------------------------------------------
-- Handler to retrieve tasks from OmniFocus that have DEVONthink link
----------------------------------------------------------------------
on GetOFDTLinkedTasks()
tell application "OmniFocus"
tell default document
set allTasks to flattened tasks
repeat with theTask in allTasks
set theFolderName to the name of the folder of the containing project of theTask
set theProjectName to the name of the containing project of theTask
set theTaskName to the name of theTask
set theNote to the note of theTask
set theNote to my trim_string(theNote, white_space, "right")
if theNote starts with "x-devonthink-item" then
-- Extract the DEVONthink UUID from the OmniFocus task's note
set theUUID to my GetDTUUID(theNote)
set taskRecord to {ofFolderName:theFolderName, ofProjectName:theProjectName, ofTaskName:theTaskName, dtUUID:theUUID}
copy taskRecord to end of taskList
end if
end repeat
end tell
end tell
return taskList
end GetOFDTLinkedTasks
----------------------------------------------------------------------
-- Handler to trim white space from a string
----------------------------------------------------------------------
on trim_string(the_string, trim_chars, trim_parameter)
set start_char to 1
set end_char to length of the_string
set all_chars to (characters of the_string)
if trim_parameter is in {"left", "both"} then
repeat with each_char in all_chars
if each_char is not in trim_chars then exit repeat
set start_char to (start_char + 1)
end repeat
end if
if trim_parameter is in {"right", "both"} then
set all_chars to reverse of all_chars
repeat with each_char in all_chars
if each_char is not in trim_chars then exit repeat
set end_char to (end_char - 1)
end repeat
end if
try
return text start_char thru end_char of the_string
on error
return ""
end try
end trim_string
----------------------------------------------------------------------
-- Handler to replace text within a string
----------------------------------------------------------------------
on ReplaceChars(this_text, search_string, replacement_string)
try
set AppleScript's text item delimiters to the search_string
set the item_list to every text item of this_text
set AppleScript's text item delimiters to the replacement_string
set this_text to the item_list as string
set AppleScript's text item delimiters to ""
return this_text
on error errStr number errorNumber
set AppleScript's text item delimiters to oldTIDs
error "Error in ReplaceChars handler. " & errStr number errorNumber
end try
end ReplaceChars