I have a growing database of RTF files that link to each other. I would like to see if a note has been linked to from another note. Does anyone know if this is possible?
- Kourosh
I have a growing database of RTF files that link to each other. I would like to see if a note has been linked to from another note. Does anyone know if this is possible?
Links aren’t indexed, therefore this isn’t supported at the moment. A probably really slow AppleScript could theoretically check this.
Hmm - ok. Thanks for the response. It would be pretty neat if this could happen one day.
Hi @Kourosh , as @cgrunenberg mentioned a script is possible but I don’t know if it works well with a lot of RTFs (as I converted all RTFs to MultiMarkdown).
Assuming your RTF records all have unique names (otherwise it will still work but then you’ll have to manually sort wrong ones out in the results) and you didn’t change names after inserting a link into a RTF record you could search for the record’s name with this script. Make sure you have two viewer windows open (with one window you would loose the selection of the RTF record).
-- Find incoming links for a RTF record via name
-- Make sure you have two viewer windows open (with only one you would loose the selection of the RTF record)
tell application id "DNtp"
try
set windowClass to class of window 1
if {viewer window, search window} contains windowClass then
set currentRecord_s to selection of window 1
else if windowClass = document window then
set currentRecord_s to content record of window 1 as list
end if
if (count of currentRecord_s) = 0 then
display notification "Please select a record"
return
end if
if (count of viewer windows) < 2 then
display notification "Please open a second viewer window"
return
end if
set theRecord to item 1 of currentRecord_s
if type of theRecord = rtf then
set theName to name of theRecord
tell viewer window 2
set search query to "text:" & theName & space & "kind:rtf"
set theResults to search results
end tell
if theResults = {} then
display notification "No links found"
return
end if
-- after getting the results it's possible to do other stuff, e.g. adding the reference URL of every record in the results as custom meta data to theRecord
else
display notification "Please select a RTF record"
return
end if
on error error_message number error_number
if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
end try
end tell
Assuming you don’t use images in your RTFs I would go another way and convert them to MultiMarkdown which would make records that link to each other searchable via their reference url which (in opposite to names) will never break. Here’s the script I used to convert RTFs to MultiMarkdown, but again, images are not supported by this and I’m not sure if all formatting is preserved! (It’s a custom script that worked well for my needs but it might need some modifications to fit yours)
If converting to MultiMarkdown is an option you could use this script to search incoming links (and don’t have to worry about changing names)
-- Find incoming links for a Markdown record via reference url
-- Make sure you have two viewer windows open (with only one you'll loose the selection of the Markdown record)
tell application id "DNtp"
try
set windowClass to class of window 1
if {viewer window, search window} contains windowClass then
set currentRecord_s to selection of window 1
else if windowClass = document window then
set currentRecord_s to content record of window 1 as list
end if
if (count of currentRecord_s) = 0 then
display notification "Please select a record"
return
end if
if (count of viewer windows) < 2 then
display notification "Please open a second viewer window"
return
end if
set theRecord to item 1 of currentRecord_s
if type of theRecord = markdown then
set theRefURL to reference URL of theRecord
tell viewer window 2
set search query to "text:" & theRefURL & space & "kind:markdown"
set theResults to search results
end tell
if theResults = {} then
display notification "No links found"
return
end if
-- after getting the results it's possible to do other stuff, e.g. adding the reference URL of every record in the results as custom meta data of the record you started from
else
display notification "Please select a Markdown record"
return
end if
on error error_message number error_number
if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
end try
end tell
Finally (and this came to my mind after all the other stuff…) if you don’t want to convert your RTFs to MultiMarkdown there should be another option: Convert to MultiMarkdown (to get the linked reference URLs instead of the names), search for linked reference URLs, set the reference URL of every result as custom meta data in the markdown record, then match the markdown record’s text content against the RTFs, set the found RTF’s meta data to the meta data of the corresponding markdown record and delete the markdown record afterwards. That could work (I think)…
Hi @pete31,
Thanks for the super-awesome response. That first script totally works like a charm.
I have been using RTF files, and I haven’t used image files. I have a couple of questions.
Thanks again!!!
If they could be optionally indexed, that would be really useful to me (and many others, I imagine).
(Just adding my vote to a possible list of new features).
Reference URL links are robust, you can always get to the linked record by clicking the link. But the other way round (finding records that link to a record) is not that easy as DEVONthink doesn’t index the RTF source (the underlying stuff where the RTF’s formatting and the “x-devonthink-item” address of the link is saved), so it’s only possible to search for the visible part of the record - and in RTFs that’s the name of the linked record.
If you (completely) change the name of the record the link is pointing to in a RTF record there’s no chance to find it.
DEVONthink uses MultiMarkdown but it’s simply called “Markdown”. One of the advantages of Markdown is that (in editing mode) everything is visible (no underlying stuff), it’s just plain text and DEVONthink indexes all of it.
A reference URL link in a RTF record is only searchable by it’s name (it’s possible to get the link address via AppleScript but it’s not possible to directly find this string in DEVONthink as it’s not indexed).
A reference URL link in Markdown (editing mode) looks like this
[Markdown link](x-devonthink-item://1519676A-9AEA-E4CF-99A3-993A49B5AE5B)
Link name and adress (the reference URL that will never break) are there in plain text and indexed in DEVONthink. You can always search for the reference URL of a record and DEVONthink will find all records that link to this record.
DEVONthink surprisingly does not preserve links, the script does. (By the way as I tried DEVONthink’s convert command and the script again I still had WikiLinks enabled and was confused as DEVONthink suddenly seemed to preserve links, but these were WikiLinks, not robust reference URL links.)
The script uses a label if an error occurs. It works over here, maybe @BLUEFROG or other users could take a look?
This is a huge advantage of using markdown imho. I have written a script in python to do what the OP requests in Ulysses - generate a list of every page linking to a selected page (and automatically create back links) - and it relies upon being able to search the URL strings themselves.
@pete31 - Thanks once again for your very thorough and thoughtful reply. I get it now that the robust link, when visible in the actual text, is actually more accessible to other programs and scripts, and therefore usable. Now to take a deep breath and convert my entire database…
@Kourosh, I totally missed the fact that my Convert to MultiMarkdown script results in links that still point to the RTF instead of links pointing to the new Markdown records (as I used it to convert RTFs that only contained normal URLs but no DEVONthink Reference URLs…)
Here’s a script that replaces the old RTF link with the Reference URL of the new markdown record (via name).
Although it throws an error if the RTF link is inside square brackets it does replace those links too (couldn’t fix the error).
The script needs the scripting additon Satimage.osax (direct download). To install it
Either drop it onto your closed System folder and it will be installed automatically, or put it in the “Scripting Additions” folder inside your System folder (How do I install a scripting addition? - Scripting Additions - MacScripter)
-- Replace RTF links in converted MultiMarkdown records (see https://discourse.devontechnologies.com/t/script-convert-rtf-to-multimarkdown/50453) with the corresponding MultiMarkdown record Reference URL (via Name)
-- This script needs the scripting additon Satimage.osax (direct download: https://www.satimage.fr/software/downloads/Satimage411.pkg)
-- Dictionary of Satimage: https://www.satimage.fr/software/en/dictionaries/dict_satimage.html
tell application id "DNtp"
try
set windowClass to class of window 1
if {viewer window, search window} contains windowClass then
set currentRecord_s to selection of window 1
else if windowClass = document window then
set currentRecord_s to content record of window 1 as list
end if
repeat with thisRecord in currentRecord_s
set theText to plain text of thisRecord
set RTFLinks to (find text "(?<=\\[).*\\]\\(x-devonthink.*(?=\\))" in theText with regexp and all occurrences)
repeat with thisRTFLink in RTFLinks
set newText to plain text of thisRecord
set theMatchResult to matchResult of thisRTFLink -- "matchResult" is a Satimage "find text" term
set d to AppleScript's text item delimiters
set AppleScript's text item delimiters to "]("
set TextItems to text items of theMatchResult
set AppleScript's text item delimiters to d
set theRTFRefURL to item 2 of TextItems
set theRTFName to item 1 of TextItems
if theRTFName ends with ".rtf" then
set theRTFNameWithoutExtension to characters 1 thru -5 in theRTFName as string
set theMultiMarkdownRecord to (item 1 of (lookup records with file theRTFNameWithoutExtension & ".md" in current database))
set theMultiMarkdownRefURL to reference URL of theMultiMarkdownRecord
set theMultiMarkdownLink to theRTFNameWithoutExtension & ".md](" & theMultiMarkdownRefURL as string
else
set theMultiMarkdownRecord to (item 1 of (lookup records with file theRTFName & ".md" in current database))
set theMultiMarkdownRefURL to reference URL of theMultiMarkdownRecord
set theMultiMarkdownLink to theRTFName & "](" & theMultiMarkdownRefURL as string
end if
set replaceTheRTFLink to change (theRTFName & "\\]\\(" & theRTFRefURL) into theMultiMarkdownLink in newText with regexp
set plain text of thisRecord to replaceTheRTFLink
end repeat
end repeat
on error error_message number error_number
if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
end try
end tell
Thanks again. I’m mainly using Find Incoming Wikilinks and a Find Incoming Direct Links scripts. I’ve now set up 2 Keyboard Maestro macros to automatically:
Find Incoming Links - Keyboard Maestro macros.zip (4.4 KB)
Now I I can just type Option-d or Option-w to run either!
Hey @pete31,
I put together a book on using DEVONthink that I’m releasing soon and included some of this thread. I wanted to send a thanks to you, but your profile is private. Could you send me a DM?
@Kourosh you re using the script provided by @pete31 in your book in the chapter Finding Return Links - AppleScripts and Smart Rules.
In that section you point out that the current script for search for finding incoming WikiLinks does not yet cover aliases, and to resolve this by manually typing the alias as search.
You can also directly add these to the search query. Do note that this assumes that all aliases are separated by semicolons only (as required in the DT3 manual).
-- Find incoming links for a Markdown record via Wikilinks
-- Make sure you have two viewer windows open (with only one you'll loose the selection of the Markdown record)
tell application id "DNtp"
try
set windowClass to class of window 1
if {viewer window, search window} contains windowClass then
set currentRecord_s to selection of window 1
else if windowClass = document window then
set currentRecord_s to content record of window 1 as list
end if
if (count of currentRecord_s) = 0 then
display notification "Please select a record"
return
end if
if (count of viewer windows) < 2 then
display notification "Please open a second viewer window"
return
end if
set theRecord to item 1 of currentRecord_s
if type of theRecord = markdown then
set theName to name of theRecord
set theAliases to aliases of theRecord
-- start Insert " | " between every alias
-- ensure double quote around each alias (for aliases of separate words)
-- Assumption: each alias is separated by semicolons
set AppleScript's text item delimiters to ";"
set theTextItems to every text item of theAliases
set theAliasesQuery to ""
repeat with textItem in theTextItems
set theAliasesQuery to theAliasesQuery & "|" & "\"" & textItem & "\""
end repeat
set AppleScript's text item delimiters to ""
-- end Insert
-- added theAliasesQuery to extend the search
tell viewer window 2
set search query to "text:" & theName & theAliasesQuery & space & "kind:markdown"
set theResults to search results
end tell
if theResults = {} then
display notification "No links found"
return
end if
-- after getting the results it's possible to do other stuff, e.g. adding the reference URL of every record in the results as custom meta data of the record you started from
else
display notification "Please select a Markdown record"
return
end if
on error error_message number error_number
if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
end try
end tell
Hey @sander2,
Thanks for that. That’s super helpful. I just gave it a run and it seems to work well. Maybe I can get it into an update.
I think Roam Research has this function and this’s the most wonderful feature for that app.
Displaying the network of connection among those notes in DEVONthink is very useful and important for researchers and students, because if one know better on the topology of the notes, S/he would understand well on those information, moreover, based on the structure of the connection between notes, it’s beneficial to carry forward the frontier of knowledge.
IMHO native support for showing backlinks would be a powerful upgrade to DEVONthink’s current WikiLink functionality as well as DT’s ability to help users find connections across different documents and ideas. I think most WikiLink users will welcome this feature.
To take this idea a bit further, it would also be nice to show a “local node graph” centered around the selected document, e.g. all the documents that are connected to the current document, and all the documents that are connected to those. I imagine such a local graph could have great potential to provide users insight on the current document. It’s like a graphical approach to the existing See Also & Classify functionality, but for explicit WikiLinks created by the user instead of content relevance calculated by the AI.
Recently there seems to be a surge of note taking apps that take the connections between notes seriously such as Roam Research and Obsidian, and the latter just released a Local Graph feature. I personally would be thrilled to see DEVONthink treat WikiLinks as a first class citizen, and automatic backlinks should be a very solid starting point
Edit: here’s Obsidian’s Local Graph functionality in action.
Perhaps a solution should be to have Devonthink generating graphs similar to the ones produced in Devonagent.
I haven’t purchased DEVONagent (yet) so I looked it up. I think that is exactly what I was thinking about, good to know that DEVONagent already lets users visualize the connections. Hopefully DT will have a similar feature.
(And another reason for me to buy DEVONagent )
Thanks @Kourosh for these macros. Besides the new snag (but with possible positive developments coming @cgrunenberg) in the system with respect to using the “Find incoming item links” script that @pete31 had generously provided to all (see Search for x-devonthink-item link not working), I have the curious problem on my end that when I run your macro for incoming wikilinks the script trips up at setting the record and sends me a notification “Please select a record” but when I run the same script manually from the menu it works perfectly. Any ideas…?
Hmm, that sounds odd. Instead of having it run the WikiLink script in Keyboard Maestro, what if you pointed it to an external Applescript file?