Populate Template Placeholders Script Automation?

Hi there, has anyone attempted to re-create Curt Clifton’s Populate Template Placeholders script for the new automation functionality? I’d love to crib it if anyone has.

1 Like

Yes, I’d love this too…
I mailed Curt directly, but unfortunately he isn’t able to create a JS version.

There’s this one wot I wrote:

Any idea how to add dates onto tasks and the project itself? E.g. Add 1 day wait before Pizza Delivery?

Sadly it doesn’t set dates, that’s one of the downsides of the template itself being a project IN OmniFocus rather than an external TaskPaper/text template.

I used to use the iOS Drafts app and then the Shortcuts app to maintain external taskpaper templates and that would allow smart date insertion. But I found I’d keep changing project/tag structures and forget to update my templates.

That’s why I’ve ended up with this solution of the templates living in the app: If for example I rename or move a tag that’s used by the template, the template gets updated by OmniFocus itself and remains valid.

If you really need smart dates then maybe something like Rosmary Orchards Drafts system (also iOS based) might be more useful: https://rosemaryorchard.com/blog/using-drafts-5-taskpaper-with-omnifocus/

1 Like

Thanks - yes looking at the Taskpaper option that looks like exactly the sort of thing I was after. Thanks!

A taskpaper option that works with JS would be top-notch as it’d be cross-platform. Shortcuts etc are unfortunately not…

Cross platform templates, with fields etc, are the one thing I really want to see automated.

I modified the example script on the Omni automation website that imports a Taskpaper file to do a find/replace (prompting the user) on variables in the file. My convention is to enclose the variables as follows: «var».

It’s just a few extra lines of code. A nice side benefit, aside from being cross-platform, is the Javascript is much faster than the old AppleScript that served the same purpose.

/*{
"author": "JBB",
"targets": ["omnifocus"],
"type": "action",
"identifier": "com.jbb.omnifocus.com.template",
"version": "1.0",
"description": "Add Template Using TaskPaper Format",
"label": "Add Template…",
"mediumLabel": "Add Template Using TaskPaper Format",
"paletteLabel": "Add Template",
}*/
(() => {
var action = new PlugIn.Action(function(selection, sender) {
	var folder = selection.folders[0]
	var folderName = folder.name
	
	var picker = new FilePicker()
	picker.folders = false
	picker.multiple = false
	var aFileType = new FileType("com.taskpaper.text")
	picker.types = [aFileType]
	var pickerPromise = picker.show()
	
	pickerPromise.then(function(urlsArray) {
		var fileURL = urlsArray[0]
		fileURL.fetch(function(data) {
			var taskPaperText = data.toString()
			var macros = taskPaperText.match(/«.*?»/g)
			if (macros != null && macros.length > 0) {
				const distinct = (value, index, self) => { return self.indexOf(value) === index }
				macros = macros.filter(distinct).map(function(x){return x.replace(/[«»]/g, '')})

				var inputForm = new Form()
				for (var i = 0; i < macros.length; i++) {
					var formInput = new Form.Field.String("var" + String(i), macros[i], "")
					inputForm.addField(formInput)
				}
				
				var formPromise = inputForm.show("Enter values for parameters:", "OK")   
	
				inputForm.validate = function(formObject) {
					return true
				}
	
				formPromise.then(function(formObject) {
					for (var i = 0; i < macros.length; i++) {
						var x = formObject.values["var" + String(i)]
						taskPaperText = taskPaperText.replaceAll('«' + macros[i] + '»', x)
					}

					taskPaperText = encodeURIComponent(taskPaperText)
					folderName = encodeURIComponent(folderName)
					var urlStr = "omnifocus:///paste?target=/folder/" + folderName +  "&content=" + taskPaperText
					URL.fromString(urlStr).open()					
				});				
			}
			else {
				taskPaperText = encodeURIComponent(taskPaperText)
				folderName = encodeURIComponent(folderName)
				var urlStr = "omnifocus:///paste?target=/folder/" + folderName +  "&content=" + taskPaperText
				URL.fromString(urlStr).open()
			}
		})
	})
});

action.validate = function(selection, sender) {
	return (selection.folders.length === 1)
};

return action;

})();

if you copy this script don’t forget the small part below the script…

2 Likes

Also a work-in-progress, but had this post saved and thought it might be worth linking my weekend project here in case it is useful to anyone else:

There are a lot of things I’d still like to add in for my own use, but I believe this more or less replicates the functionality in Curt Clifton’s original AppleScript.

3 Likes

Thanks, Kaitlin! I look forward to checking this out.

Does your plug-in handle date math (i.e. relative defer/due date calculations) the way that Curt Clifton’s AppleScript does? If not, please consider this a feature request. 😃

Hi Tim!

I haven’t used Curt’s script myself for some time, but I believe it does based on his description.

I haven’t tested extensively yet, obviously, so please let me know if anything doesn’t work as expected!

(I would actually like to add an option to specify due dates without setting the due date on the template itself, ultimately.)

Thanks for your reply, @kaitlin. I’ll be sure to check out your plug-in sometime soon…and will let you know if anything doesn’t work as expected.

(I would actually like to add an option to specify due dates without setting the due date on the template itself, ultimately.)

Having an option to specify due dates without including due dates in the template would be very helpful. As it stands, when creating templates, I use defer/dues that are far into the future (e.g. Jan 31, 2100).

For those interested, I altered @JBB’s script above to work without the need for a folder selection and to place the new project at top-level in the document tree.

/*{
"author": "JBB",
"targets": ["omnifocus"],
"type": "action",
"identifier": "com.jbb.omnifocus.com.template",
"version": "1.0",
"description": "Add Template Using TaskPaper Format",
"label": "Add Template…",
"mediumLabel": "Add Template Using TaskPaper Format",
"paletteLabel": "Add Template",
}*/
(() => {
var action = new PlugIn.Action(function(selection, sender) {
    var picker = new FilePicker()
    picker.folders = false
    picker.multiple = false
    var pickerPromise = picker.show()

    pickerPromise.then(function(urlsArray) {
        var fileURL = urlsArray[0]
        fileURL.fetch(function(data) {
            var taskPaperText = data.toString()
            var macros = taskPaperText.match(/«.*?»/g)
            if (macros != null && macros.length > 0) {
                const distinct = (value, index, self) => { return self.indexOf(value) === index }
                macros = macros.filter(distinct).map(function(x){return x.replace(/[«»]/g, '')})

                var inputForm = new Form()
                for (var i = 0; i < macros.length; i++) {
                    var formInput = new Form.Field.String("var" + String(i), macros[i], "")
                    inputForm.addField(formInput)
                }

                var formPromise = inputForm.show("Enter values for parameters:", "OK")

                inputForm.validate = function(formObject) {
                    return true
                }

                formPromise.then(function(formObject) {
                    for (var i = 0; i < macros.length; i++) {
                        var x = formObject.values["var" + String(i)]
                        taskPaperText = taskPaperText.replaceAll('«' + macros[i] + '»', x)
                    }

                    taskPaperText = encodeURIComponent(taskPaperText)
                    var urlStr = "omnifocus:///paste?target=projects&content=" + taskPaperText
                    URL.fromString(urlStr).open()
                });
            }
            else {
                taskPaperText = encodeURIComponent(taskPaperText)
                var urlStr = "omnifocus:///paste?target=projects&content=" + taskPaperText
                URL.fromString(urlStr).open()
            }
        })
    })
});

return action;
})();

EDIT: One note. To make this work on iOS, the taskpaper template files need to be .txt files and not .taskpaper. iOS does something weird there and doesn’t let you select them.

3 Likes

I remember having a related issue in the past, It seemed to matter if you have an application registered to handle the extension. I can’t actually remember what I was doing but when I uninstalled an app that handled taskpaper files, unrelated shortcuts that handled the files broke.

1 Like

Greetings all! Excellent concepts and ideas.

Just a heads-up that the FileType class has been renamed to the TypeIdentifier class, but old terms should continue to work.
https://omni-automation.com/shared/filetypes.html

var picker = new FilePicker()
picker.folders = false
picker.multiple = false
var aType = new TypeIdentifier("com.taskpaper.text")
picker.types = [aType, TypeIdentifier.plainText]
var pickerPromise = picker.show()

pickerPromise.then(urlArray => {
	var fileURL = urlArray[0]
	fileURL.fetch(data => {
		var importedText = data.toString()
		
		var openTag = "«"
		var closeTag = "»"
		var expression = new RegExp(openTag + ".*?" + closeTag, "g")
		var placeholders = importedText.match(expression)
		
		if (placeholders != null && placeholders.length > 0){
			var placeholders = Array.from(new Set(placeholders))
			
			console.log(placeholders)
		
		} else {
			console.error("Imported text contains no placeholders.")
		}
	})
})

is the only thing you removed the folder select? (cause I’m looking to add that and can then take @JBB’s original :-) )

I believe so. I had also altered the file type piece so that I had more control of the selection process.

1 Like

Ah ok, I’ll muddle along some more then ;-)
Folder select is more challenging than expected, but a good exercise to get to know the syntax.

Tested this out today.

  1. Is it possible to allow invoking the script even if something is selected?
  2. Curt had things set up to keep the Templates folder hidden so you can keep the defer/due dates set on items in the templates, without them affecting other Perspectives throughout the app (by dropping the Templates folder, but leaving the Projects within it alone) - could be a bug, but it works. I modified the script to replace: let templateFolder = foldersMatching("Templates")[0]; with let templateFolder = Folder.byIdentifier('xxxxxx);

I had made a few patches to Curt’s script. I’ll have to eventually update this one, I’ll submit the changes via GitHub. Thanks for the start on this!