End an action, yet still honor the AFTER SUCCESS configuration?

Is there a way to conditionally end an action, yet still honor the AFTER SUCCESS configuration?

I have a multi-step action that includes this JavaScript at the end of the second-to-last action. The last action is an HTML Preview step.

		if (previewListAfterSuccess) {
			// Refer to the next action step: HTML Preview
			draft.setTemplateTag("list_content", d.content);
		} else {
			// Do not continue with the next action step.
			context.cancel();
		}

The context.cancel() above does end the action, but the AFTER SUCCESS configuration is ignored because of the cancel.

Thanks in advance!

Possibly the easiest way is to script the „after success“ stuff into the else path here. (I suppose you don’t want to execute the action steps afterwards).

So e.g. if you want to set tags or archive the draft you can do that just within the script Draft | Drafts Script Reference

please note: if you’re planning to share this action you shouldn’t do this approach as @sylumer explained below

1 Like

The only action context options are succeed (which is implicit), cancel, and fail (which are both explicit). Succeed will continue through the remaining steps. Cancel and fail are explicitly saying do not continue (and specifying a type of stop for notification/logging).

There is no branching or step-skipping, just a linear sequence when it comes to action steps; unless for example you programmatically set template tags and then use those tags to dynamically call other actions by name. But that requires multiple actions to accommodate.

What I would recommend doing is redesigning that last part of your action and keeping your logic inside of the script action.

In Drafts scripting, I think I’m right in saying you can do anything you can do in an action step. If there is something you cannot do, I don’t think I’ve needed to do it yet. So what you would want to do is bring that final HTML Preview step backward into the script step and into the “true” part of your if statement. I would remove the else part entirely as you do not want to cancel, you wish to succeed whether or not your preview is to be shown.

There is an example at the top of the HTMLPreview class reference that you can use as the basis for preparing your own scripted HTML Preview.

If you have a complex set of HTML or CSS, consider defining the content within a custom template tag action step earlier in one or more custom template tags earlier in the script, and then using draft.processTemplate() to construct the preview HTML string from static HTML and template tags. I find it leaves the code looking less cluttered that way and makes your HTML/CSS a little bit easier to maintain because it is not wrapped in JavaScript.

I would not take this approach. The reason being the end user could change what the after success processing is on an action (archive, add a tag, etc.), I don’t think you could accommodate that easily through adding code in. It would only be possible if you did not expect the end user to ever set any after success action - which wouldn’t be a good design for the action.

Imagine you do this, then the end user changes it, and the result is it still sometimes never applies, and when it does, it may even conflict or combine with what the action is doing.

Hope that all makes sense and helps.

3 Likes

Absolutely, I edited my reply above. I assumed it was just for personal use :slight_smile:

1 Like

Just to chime in another option, you could have a separate preview action and queue it to run after the current action with:

let a = Action.find("My-Preview-Action")
app.queueAction(a, d)
1 Like

Hi, @FlohGro, @sylumer, and @agiletortoise. Thanks for taking the time to help. Sorry for the delayed reply.

@sylumer, this suggestion seems like a great approach. But unfortunately when I tried various combinations of draft.setTemplateTag(), draft.processTemplate(), HTMLPreview.create(), and preview.show(), I’ve come up short. Clearly I’m missing something fundamental.

Might you be able to point me to an example that uses your suggested approach? Note that the list that I’m attempting to preview is a draft in markdown format.

Thank you!

The HTMLPreview is pretty straightforward, and there’s demo code on that page. It takes HTML, and displays it in a preview window.

I assume where you are stuck is constructing the HTML? This would be an example with a template in a string variable:

const template = `<html>
<head><title>Title</title></head>
<body>
%%[[draft]]%%
</body></html>`

// pass template through template engine to replace tag values
const html = draft.processTemplate(template)

// show that html in a preview
let preview = HTMLPreview.create();
if (preview.show(html)) {
    // continue button was pressed
}
else {
    // cancel button was pressed
}

I think @sylumer was suggesting it would be easier to manage your preview template in a “Define Template Tag” step earlier in the action. If you did that, you could retrieve the template by evaluating the tag. So if you had a define step for the tag “my-template”, you could get that value to use as your template for the preview like:

const template = draft.getTemplateTag("my-template")
1 Like

Here’s an example that holds the CSS in one tag, the Markdown in another tag, but also pulling in the CSS and some Drafts data through a standard tag too.

It uses the conditional structure to choose whether or not to show the preview - in this case based on choice via a prompt. If no preview is chosen, then I’ve added in a message just so you can see it is processing.

Hope that helps.

1 Like

Hi, @agiletortoise. Thanks for the reply!

No, there’s another issue (or misunderstanding on my part) that I’ll detail below in my reply to @sylumer.

Hi, @sylumer. Thanks for the reply and example! That seems to work reliably and I understand the code you provided. Much appreciated!


Unfortunately, I’m seeing an intermittent issue when I attempt to set the markdown within the code. Here’s a simple example:

const md = "## Title";
draft.setTemplateTag("my_markdown", md);

// Prep Markdown to HTML conversion using the MMD engine
let mmdContent = MultiMarkdown.create();
mmdContent.format = "html";

// Show the preview of the rendered content based on the content specified by tags in the action
let hpMain = HTMLPreview.create();
hpMain.show(mmdContent.render(draft.processTemplate("[[mdcontent]]")));

Sometimes the above yields:


But other times:


I haven’t been able to find a pattern. Could there be a timing issue? Or am I missing something fundamental?


I’ve uploaded this example problematic action: Action for Jim-v2 | Drafts Directory

I’ve never seen a timing issue on that. You could try explicitly splitting out the HTML generation strps from the display to see if that makes any difference.

// Show the preview of the rendered content based on the content specified by tags in the action

let strMMD = draft.processTemplate("[[mdcontent]]");

let strHTML = mmdContent.render(strMMD);

let hpMain = HTMLPreview.create();
hpMain.show(strHTML);
1 Like

Thanks for that suggestion, but I had tried that and saw the same inconsistent behavior.

Ok. I was able to reproduce it trying your action and I think I see what the issue was.

Try this one and see if your results are better.

I reckon you’ll get what was going on timing-wise once you see my change; assuming it works for you too that is :grin:

1 Like

If you want to retrieve and use as a template a value you created previously in a define template tag, you should retrieve its value using drafts.getTemplateTag – and then pass that value through the template processor.

It’s a subtle difference, but try:

let strMMD = draft.getTemplateTag("mdcontent");

let strHTML = mmdContent.render(strMMD);

let hpMain = HTMLPreview.create();
hpMain.show(strHTML);

You might just want the template to incorporate your Markdown processing where appropriate, because I don’t think you really want to be passing your full HTML template through the Markdown processor – but rather just the actual Markdown content. You do that by wrapping values in the template with %% %%. So you can just get the template value, and call processTemplate on it without the extra Markdown renderer steps.

1 Like

On that particular point, doesn’t Markdown include HTML? I.e. Markdown as a format recognises HTML as valid, and renderers just skip it leaving it unchanged when transforming Markdown to HTML.

When writing in Markdown for me website it isn’t uncommon for me to have to put some HTML amongst the more typical Markdown in my Markdown file to get something to render how I want.

A little off-topic, but I guess it depends. Markdown is designed to support inline content HTML, but not necessarily structural HTML – and it’s supposed to be separated block-level elements.

Most newer, more advanced parsers, like MultiMarkdown and mark in Drafts, are pretty savvy about ignoring and leaving intact various HTML, but you can get some weird results even with them, and other parsers don’t handle it well at all.

For example, if you run this text:

<html>
<head>
<title>WAT</title>
</head>

<body>

## My Markdown

This *is* it.

</body>

</html>

Through Gruber’s original Markdown parser, you get this:

<p><html>
<head>
<title>WAT</title>
</head></p>

<p><body></p>

<h2>My Markdown</h2>

<p>This <em>is</em> it.</p>

<p></body></p>

<p></html></p>

Which I’m pretty sure no one wants.

So I’d consider it best-practice not to run non-content elements through the parser.

2 Likes

Thanks again, @sylumer! Yes, that works consistently for me. It seems it’s really a matter of order, not so much a timing issue. Agree?

Agree; thanks, @agiletortoise!

In fact it seems that I don’t need to render the markdown in the code since %%[markdown]%% within a Define Template Tag action step seems to work as expected (assuming I order the action steps properly as @sylumer demonstrated above.)

That is, …

Script (above the Define Template Tag(s))

const md = "## Title";
draft.setTemplateTag("my_markdown", md);

Define Template Tag; Tag:[[htmlpreview]]

<style>
[[css]]
</style>

%%[[my_markdown]]%%

Script (below the Define Template Tag(s))

let hpMain = HTMLPreview.create(); 
hpMain.show(draft.processTemplate("[[htmlpreview]]"));

Putting it all together, here’s a version that is simple and works reliably: Action for Jim-v4 | Drafts Directory

I think if it was purely order, the results would have been consistent - alway work or always fail. If there was a timing element to it and moving it to the start changed the timing enough to deal with any race condition on setting the tag, that could explain the result.

1 Like

If you want a technical explanation, Drafts template engine is not recursive, a tag cannot return additional tags and expect them to be evaluated. Tags are evaluated sequentially, but not in any guaranteed order. So what you really want to do is for this case is the below - where you retrieve the value of the template and use it, not rely on tag evaluation to insert it’s content:

let hpMain = HTMLPreview.create(); 
let template = draft.getTemplateTag(“template”)
hpMain.show(draft.processTemplate(template));
1 Like

Thanks for that information, @agiletortoise. Following your guidance, here’s the revised action: Action for Jim-v5 | Drafts Directory