AppleScript: create new markdown record with selected records as transclusions

Provoked by this thread I have created the following script—which is quite useful for me and may possibly be of interest to some others here.

In essence, the script creates a new markdown record (you can specifiy the name or accept the default) then creates items links to be included in the new record as transclusions. There is a test to ensure you have selected only transcludable (sorry if the word doesn’t exist—you know what I mean) records.

The assembly is carried out by the very short and simple handler at the end of the script so you can easily change the formatting of the split between the transcluded items.

Warning: it works for me, but I’m a mere amateur so please try if first on some dummy records to ensure it does what you want.

(* This script creates a new markdown record in the chosen database
and group and then adds to that record transclusion item links for plain text,
markdown or HTML records you have selected in the database with a line drawn
between each item. It will prompt you for the name of the new record or
you can choose to use the default name. *)

-- the database to open
property pDatabase : "/Users/[path to database you wish to use]]/`[name of database]].dtBase2"
-- the group to use for tbe markdown file of transclusions
property pGroup : “[UUID of the Group in which you wish to create new record]”
tell application id "DNtp"
	try
		activate
		-- open the database
		set theDatabase to open database pDatabase
		set myGroup to get record with uuid pGroup
		
		--check that we have some records selected for transclusion
		set theSelection to the selection
		if (count of theSelection) is 0 then
			error "Please some markdown records for transclusion"
		end if
		
		--set up the new record for the transclusions
		set dAnswer to "New document"
		set userEntry to display dialog ¬
			"Enter the name of the new document (without the extension) or accept the default: " default answer dAnswer with title "New document for the transclusions"
		set dAnswer to text returned of userEntry
		set theNewRecord to create record with {name:dAnswer, type:markdown} in myGroup
		
		--Get the UUIDs of the selected records, onvert them to item links
		--and format the transclusions
		set theText to plain text of theNewRecord
		repeat with theRecord in (selection as list)
			set theType to (type of theRecord) as string
			--Remember, you can use only markdown, plain text or HTML files in transclusion
			if theType is in {"markdown", "txt", "html"} then
				set theUUID to uuid of item 1 of theRecord
				set transcludedItem to my createTransclusion(theUUID)
				set theText to theText & transcludedItem
				set plain text of theNewRecord to theText
			else
				error "Check selected records: only markdown, plain text and HTML records are suitable for transclusion"
			end if
		end repeat
		set root of viewer window 1 to myGroup
		
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
		return
	end try
end tell

--The handler to format the transclusion links is separate simply to make it easy
--to change the formatting between items if you wish.
on createTransclusion(theId)
	set theLink to "{{" & "x-devonthink-item://" & theId & "}}" & return & "---" & "  " & return
	return theLink
end createTransclusion

Stephen

2 Likes

Very nice little script!

One thing you could modify, though it’s not wrong how you did it…

With any DEVONthink record there is a UUID which is part of its reference URL.
So instead of getting the UUID, you could have gotten the reference URL and avoided having to hard-code the x-devonthink-item://. in the creation handler.

set recURL to reference URL of theRecord
……………
set theLink to "{{" & recURL & "}}" & return & "---" & "  " & return

Oh, that’s really helpful: thanks. I also use the same structure in another of my journal scripts and what you suggest is really much neater. I’ll amend both scripts and much appreciate the observation.

Stephen

You’re welcome!

Would you care for another small suggestion?

Yes, by all means: I learn so much from this forum and need as much learning as I can get! :grinning:

Stephen

1 Like

Instead of updating the text on every repeat, you could cache the updated text into a list then update the text at the end.

Here is a simple example…

property validTypes : {"markdown", "txt"}

tell application id "DNtp"
	set textUpdates to {} -- Set up an empty list to populate with new strings, etc.
	set currentRecord to content record -- Get the current doc
	if (type of currentRecord as string) is in validTypes then -- Check the document type
		set currentText to (plain text of currentRecord & linefeed) -- Get the plain text of the file and append a linefeed
		repeat with a from 1 to 10 -- A simple counter for the example
			copy ("[" & a & "](https://www.devontechnologies.com)" & linefeed) to end of textUpdates -- Copy the string to the list, noting a linefeed is added after each line
		end repeat -- Keep adding to the list until the loop expires
		set plain text of currentRecord to (currentText & (textUpdates as string))  -- Update the text of the file in one move
	end if
end tell

Original document

After updates

1 Like

With a big debt of gratitude owed to my teacher and mentor, @Bluefrog :pray:, here is a refined version 2.0 of the script. It now uses set recURL to reference URL of theRecord to obtain the DT item link directly (and hence dispense with construction of the link, as in version 1.0). In particular (with a noticeable increase in speed when many records are selected for transclusion) it incorporates an empty list for the item links (set textUpdates to {} and set plain text of theNewRecord to (textUpdates as string)) so enabling a single update of the new record when all item links have been gathered.

(* This script creates a new markdown record in the chosen database
and group and then adds to that record transclusion item links for plain text,
markdown or HTML records you have selected in the database with a line drawn
between each item. It will prompt you for the name of the new record or
you can choose to use the default name. *)

-- the database to open
property pDatabase : "/Users/[path to database you wish to use]/*.dtBase2"
-- the group to use for tbe markdown file of transclusions
property pGroup : “[UUID of relevant group]”
property validTypes : {"markdown", "txt", "html"}

tell application id "DNtp"
	try
		activate
		-- open the database
		set theDatabase to open database pDatabase
		set myGroup to get record with uuid pGroup
		
		--check that we have some records selected for transclusion
		set theSelection to the selection
		if (count of theSelection) is 0 then
			error "Please select some markdown records for transclusion"
		end if
		
		--set up the new record for the transclusions
		set dAnswer to "New document"
		set userEntry to display dialog ¬
			"Enter the name of the new document (without the extension) or accept the default: " default answer dAnswer with title "New document for the transclusions"
		set dAnswer to text returned of userEntry
		set theNewRecord to create record with {name:dAnswer, type:markdown} in myGroup
		
		set textUpdates to {} -- Set up an empty list to populate with new strings, etc.
		repeat with theRecord in (selection as list)
			if (type of theRecord as string) is in validTypes then -- Check the document type
				--Get the reference URLs of the selected records, convert them to item links
				--and format the transclusions
				set recURL to reference URL of theRecord
				set transcludedItem to my createTransclusion(recURL)
				copy (transcludedItem & linefeed) to end of textUpdates -- Copy the string to the list, noting a linefeed is added after each line
			else
				error "Check selected records: only markdown, plain text and HTML records are suitable for transclusion"
			end if
		end repeat -- Keep adding to the list until the loop expires
		
		set plain text of theNewRecord to (textUpdates as string) -- Update the text of the file in one move
		
		set root of viewer window 1 to myGroup
		
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
		return
	end try
end tell

--The handler to format the transclusion links is separate simply to make it easy
--to change the formatting between items if you wish.
on createTransclusion(theURL)
	set theLink to "{{" & theURL & "}}" & linefeed & linefeed & "---" & linefeed
	return theLink
end createTransclusion

Stephen

1 Like

Thanks for sharing @Stephen_C

There seems to be a typo in this line:

property pGroup : “[UUID of relevant group]”

After replacing the (“) character with the standard apostrophes ("), the script works perfectly.

Apologies: at some stage a set of smart quotation marks crept in there—which is very clearly not smart in a script.

Stephen

1 Like

@Stephen_C Very nice script!

I wanted to learn a little Applescript, so I decided to modify it to put the New Document in the group enclosing the first selected item.

Also, it looked like this was where I needed to hardcode the database and the UUID of the group for New Document:

property pDatabase : "/Users/[path to database you wish to use]/*.dtBase2"
-- the group to use for tbe markdown file of transclusions
property pGroup : “[UUID of relevant group]”

Here’s what I ended up with and it works just like I want except for one thing.

If I run it from the Script Editor, all’s well. If I run it from Devonthink’s script menu it always posts the error “check selected records: only markdown, etc.”

It doesn’t appear to have the right window in front unless I run it from the Script Editor. That’s my guess, anyway.

I hate to ask for handholding. Mostly I’m curious where I went wrong. I think I need something more than just “activate” to get back to the selected records in DT.

Any comments most welcome!

property validTypes : {"markdown", "txt", "html"}

tell application id "DNtp"
	try
		activate
		--check that we have some records selected for transclusion
		set theSelection to the selection
		if (count of theSelection) is 0 then
			error "Please select some markdown records for transclusion"
		end if
		set theMaster to item 1 of theSelection
		set theDatabase to current database -- of theMaster
		set myGroup to current group
		--set up the new record for the transclusions
		set dAnswer to "New document"
		set userEntry to display dialog ¬
			"Enter the name of the new document (without the extension) or accept the default: " default answer dAnswer with title "New document for the transclusions"
		set dAnswer to text returned of userEntry
		set theNewRecord to create record with {name:dAnswer, type:markdown} in myGroup
		
		set textUpdates to {} -- Set up an empty list to populate with new strings, etc.
		repeat with theRecord in (selection as list)
			if (type of theRecord as string) is in validTypes then -- Check the document type
				--Get the reference URLs of the selected records, convert them to item links
				--and format the transclusions
				set recURL to reference URL of theRecord
				set transcludedItem to my createTransclusion(recURL)
				copy (transcludedItem & linefeed) to end of textUpdates -- Copy the string to the list, noting a linefeed is added after each line
			else
				error "Check selected records: only markdown, plain text and HTML records are suitable for transclusion"
			end if
		end repeat -- Keep adding to the list until the loop expires
		
		set plain text of theNewRecord to (textUpdates as string) -- Update the text of the file in one move
		
		-- set root of viewer window 1 to myGroup
		
	on error error_message number error_number
		if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
		return
	end try
end tell

--The handler to format the transclusion links is separate simply to make it easy
--to change the formatting between items if you wish.
on createTransclusion(theURL)
	set theLink to "{{" & theURL & "}}" & linefeed & linefeed & "---" & linefeed
	return theLink
end createTransclusion

First, thanks for the kind comment about my original script. I fear I’m a little rusty with this AppleScript at the moment as I’ve not looked at it for a long time…which is a rather pathetic excuse for saying I don’t know why you have you current problem!

Sorry not to help—but be consoled much better programmers than I will be along soon to provide the necessary guidance.

Stephen

Taking the bait, I came up with this version in JavaScript

(() => {
	const app = Application("DEVONthink 3");
	app.includeStandardAdditions = true;
	if (app.selectedRecords.length === 0) {
	  error("Please select some markdown records for transclusion");
	}
	const masterRecord = app.selectedRecords()[0];
	const masterGroup = masterRecord.locationGroup();
	const newFilename = (app.displayDialog("Enter the name of the new document (without the extension) or accept the default:",
	{defaultAnswer: "New document",
	 withTitle: "New document for the transclusion"})).textReturned;
    const newRecord = app.createRecordWith({type: "markdown", name: newFilename}, 
	     {in: masterGroup});  
    newRecord.plainText = app.selectedRecords().filter(r => 
	   r.type() === "markdown" || r.type() === "txt" || r.type() === "html")
	   .map(r => `{{${r.referenceURL()}}}`).join('\n\n---\n');
})()

Seems to work ok in Script Editor. Does not use a master database (which was never used in the original script, either).

Wonderful! JavaScript has many advantages over Applescript, in my unwashed opinion, anyway.

I had to make a couple of changes. The last join starts with a backtick, which should be a straight single quote.

I also took out the const keyword on the line setting newRecord.plainText.

After that, it ran from Script Editor and DT script menu alike!

My use case, by the way, was work notes about tasks. There were six or eight of them with two that were duplicates and two that called for conflicting resolutions.

A call was getting lost in a maze of ticket numbers, so I manually transcluded them all into one note.

I create notes with a Keyboard Maestro macro that puts a link to the note itself at the top.

That lets me go from an aggregate, transcluded note back to any one of the source notes for update.

There was also a much more compelling reason, beyond merely getting work done. I wanted to learn a little more about automating Devonthink.

Which is a long way of saying thank you for your help!

I appreciate your original inspiration, and it helped me learn a little more about Applescript.

Thanks, Stephen!

1 Like

Forgot - that master database variable was a remnant from an earlier attempt. Lint, as a C programmer would say.

Sorry for the typos. As usual, these were „easy“ last minute changes :sweat_smile:

I fixed those typos in the script above, now.

1 Like

That cures something else I’d noticed - when selecting documents in more than one group, it only processed the selection from one group.

A quick test indicates it is handling files selected in multiple groups now.

Many, many, thanks. This is going to be very handy.

Better than combining files in my use case, because I can edit individual files without having to regen the aggregation.