Include Action vs. separate actions

I use the New Draft with Template Metadata from @agiletortoise to create a new draft from template. I have created several actions which then insert strings into the new draft at desired locations based on selections from a prompt. These actions work as expected when invoked individually, one-by-one, on the draft created from the template. However, if I create a separate actions which includes these actions using the Include Action functionality, my scripts act as if the editor is empty, rather than containing the new draft from template. I can’t figure out why this doesn’t work as I expect.

An example action is shown below. This inserts the day of the week, after the string “Day:” in the new draft from template.

//Function returns current day of week
function findDayofWeek() {
let d = new Date();
let day = d.getDay();
var dayOfWeek = [" Sunday", " Monday", " Tuesday", " Wednesday", " Thursday", " Friday", " Saturday"];
let currentDay = dayOfWeek[day];
return currentDay;
}

// Find cursor insertion point using RegEx
// Function to escape special characters
function escapeRegExp(stringToGoIntoTheRegex) {
return stringToGoIntoTheRegex.replace(/[-/\^$*+?.()|[]{}]/g, ‘\$&’);
}

// Function to insert cursor after defined text in draft
// uses function to escape special characters in the string
function cursorInsert(stringToInsertAfter) {
var textToFind = stringToInsertAfter;
var stringToGoIntoTheRegex = escapeRegExp(textToFind); //escape the string
var textName = new RegExp(stringToGoIntoTheRegex); //regex for the string
var textLength = textToFind.length;
var d = draft.content;
var textStart = d.search(textName); //find start of the string

editor.setSelectedRange(textStart+textLength,""); //insert cursor at string end
editor.activate();	

}

var stringToInsertAfter = “Day:” //string to find in the draft, hardcoded
cursorInsert(stringToInsertAfter); // fnc puts cursor after chosen string

var dayOfWeek = findDayofWeek();
editor.setSelectedText(dayOfWeek);
editor.activate();

It is hard to see what is going on without being able to see the exact details of the interactions you have set-up. It is useful to share links to *all* actions (parent and all child actions it includes) so that others can download the exact actions, reproduce the behaviour and analyse what is going on.

Unfortunately I can’t find an action called “New Draft with Template Metadata” in the Action directory so even assuming just some basic setup isn’t possible.

My initial guesses would be working on the wrong draft (current draft vs newly created draft) or a draft that hasn’t had an explicit update so no positioning can be found. But at the moment they are just guesses.


Tip: Wrap your code between triple back ticks to create a code block. It makes it easier to read as formatting is retained and some syntax highlighting is applied. Also it stops the forum software misinterpreting and misformatting the code.

Thank you for the helpful tips. Let me try again.

The initial action is “New Draft with Template” from @ agiletortoise, New Draft with Template | Drafts Action Directory. I had apparently renamed it.

This action places a chosen template as the active draft. This is the exact copy of my template:

Daily Stuff
Date:[[date|%Y-%m-%d]]
Day:

[[Activity]]:<|>
[[Fitness]]:


[[template/daily]]:

I have an action which inserts the day of the week, Insert Day of Week | Drafts Action Directory.

Invoking that action, after initially running “New Draft with Template” will insert the current day of the week immediately after the string “Day:” in the above draft, as expected.

I created an action which sequentially calls these two actions using Include Action steps (test | Drafts Action Directory). This does not insert the day of the week at the proper location.

I can’t figure out why these two different techniques of invoking the same actions sequentially do not produce the same output.

Okay, I think I have a reasonable grip on what’s going on. let’s walk it through.,

The initial template population action runs against a draft, let’s call it D1 for convenience, but the draft it builds is another draft, let’s call it D2. At the end of that action D1 is unloaded from the Drafts editor, and D2 is loaded in its place.

Now the action is still running against the original D1 draft when the second included action is triggered.

The action contains the following code.

// Function to insert cursor after defined string in draft
// uses function to escape any special characters possibly in the string
function cursorInsert(stringToInsertAfter)  {
	var textToFind = stringToInsertAfter;
	var stringToGoIntoTheRegex = escapeRegExp(textToFind); //escape the string
	var textName =  new RegExp(stringToGoIntoTheRegex);  //regex for the string
	var textLength = textToFind.length;
	var d = draft.content;
	var textStart = d.search(textName);  //find start of the string

	editor.setSelectedRange(textStart+textLength,""); //insert cursor at string end
	editor.activate();	
}

What I’m going to focus on initially are the following lines:

	var d = draft.content;
	var textStart = d.search(textName);  //find start of the string

We can see d being set to the content of draft. Now draft is the draft the overall action was being run against, which is D1. Importantly, that is not D2, which is the draft we want it to be running against.

The first mistake is essentially that even though this function is operating on the editor, the code initialises based on the draft; which we can see is not always the same thing.

A simple change to the code to use the editor object instead resolves this and gets more or less everything working.

// Function to insert cursor after defined string in draft
// uses function to escape any special characters possibly in the string
function cursorInsert(stringToInsertAfter)  {
	var textToFind = stringToInsertAfter;
	var stringToGoIntoTheRegex = escapeRegExp(textToFind); //escape the string
	var textName =  new RegExp(stringToGoIntoTheRegex);  //regex for the string
	var textLength = textToFind.length;

	let textStart = editor.getText().search(textName);  //find start of the string

	editor.setSelectedRange(textStart+textLength,""); //insert cursor at string end
	editor.activate();	
}

But, that isn’t quite the whole picture. When I originally ran the action, I got this in the first couple of lines.

**D Sundayaily Stuff**
Date:2021-02-21

i.e. Sunday had being inserted after the first three characters of the editor content (draft D2).

The logical assumption might then be that the original draft (draft D1) had “Day:” at the start of it (“Day:” being the parameter passed into the function above, but that’s four characters long, and my D1 was actually blank.

Repeating but with a starting draft containing “Day:”, the insertion was applied at the expected location. Therefore, the issue lies when “Day:” isn’t found.

The key point is that the search() function returns -1 if the searched for text is not found. But that isn’t catered for in the function above, and since the length of “Day:” is four characters, that gives us a positive offset of three characters, hence the insertion three characters after the start of the editor content. While this might not matter too much with the fix above as the template would always have that text in, when run stand alone, that is not always the case.

On that basis , I think that the code for this action, could be amended and rationalised to something like the following.

// Function returns current day of week
function findDayofWeek() 	{
	let d = new Date();
	let dayOfWeek = [" Sunday", " Monday", " Tuesday", " Wednesday", " Thursday", " Friday", " Saturday"];
	return dayOfWeek[d.getDay()]; //current day of week
}

// Function to escape special characters
function escapeRegExp(stringToGoIntoTheRegex) {
    return stringToGoIntoTheRegex.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}

// Function to insert cursor after defined string in draft
// uses function to escape any special characters possibly in the string
function cursorInsert(stringToInsertAfter) {
	let textName =  new RegExp(escapeRegExp(stringToInsertAfter));  //regex for the string
	let textStart = editor.getText().search(textName);  //find start of the string
	if(textStart == -1) return false;
	editor.setSelectedRange(textStart + stringToInsertAfter.length, ""); //insert cursor at string end
	return true;
}

let stringToInsertAfter = "Day:"
if (cursorInsert(stringToInsertAfter))	editor.setSelectedText(findDayofWeek());
editor.activate();

I think that covers everything for this particular instance. It may be that other actions are giving you issues for different reasons. If you can’t spot why, post back with the details.

That is extremely helpful and really increases my understanding of the how the environment works. Thank you for taking the time to examine the code and provide such a detailed and understandable explanation.

My problem, as you concluded, was not fully understanding the difference between the draft and editor objects. My other actions now run correctly after fixing this issue.

Since you have already examined this code and have an understanding of the process, I am going to take the liberty of asking you one more general question:

What you fixed is an example of a more general workflow I have been trying to optimize:

  1. Create a boilerplate template
  2. Add specific, variable data (strings) to that template (such as the day of the week in the example you fixed; or strings chosen from a prompt).
  3. Insert the variable data (strings) into the template for the final output.

The current insertion technique is to search for a known string in the template and insert the desired variable data (strings) after that string. This seems like it would be a common workflow and someone has probably solved it in a more elegant and general way. My approach feels a little clunky. Do you know of examples of similar solutions/workflows that I might study and learn from, or do you have any suggestions yourself for a better strategy?

Thanks again for your generosity.

1 Like

Anything that can utilise Drafts own template tags could be dealt with from the original template as that initial action can process them.

For example [date|%A] represents the full name of the current day of the week and could be included in your template.

For non-Drafts data, then you probably want to look at iteration through placeholder. There have been a few of these posted, but for flexibility, maybe check out this one.

Using custom template tags might be an alternative to my cursor insertion method.

I had stumbled across the Mustache stuff previously but not taken the time to learn more. This looks like it may be a more powerful, general solution - worth spending some time learning. Thanks for your many insights.

Using Drafts custom template tags might be adequate and easier to work into the existing functionality. In the “New Draft with Template” script, you could modify this area of the script to add some custom tag values:

let d = Draft.create();
for (let tag of template.tags) {
	if (tag != "template") {
		d.addTag(tag);
	}
}
d.languageGrammar = template.languageGrammar;

// BEGIN CUSTOM TEMPLATE TAGS
d.setTemplateTag("mytag", "my value");
d.setTemplateTag("myothertag", "my other value");
// END

d.content = d.processTemplate(template.content);

Then if you had [[mytag]] in the template, it would get replaced with my value, etc.

After determining an insertion point in the template, I insert various strings selected from a prompt using:

{{{editor.setSelectedText(p.fieldValues["myActivities"]);

Using your example, I would have a custom tag [[activity]] in the template that would be replaced with:

d.setTemplateTag("activity", p.fieldValues["myActivities"]);

The custom tag then serves as the “insertion point finder”, so to speak, in the template. This seems like a more repeatable and understandable implementation that what I’m currently doing.