Omni Automation Script URLs
Updates to the suite of Omni applications include changes to the security architecture regarding the execution of external Omni Automation scripts via URLs, such as those generated and run by 3rd-party applications and webpages. Specifically, the ability to use an application preference to control the security interaction with external scripts has been discontinued. This change is reflected in the following application release note:
Remote Script Invocation — Replaced the OJSBypassPreviews preference with support for pre-approving individual scripts. Remote scripts are also disabled by default now, with an interface in the Omni Automation configuration area to enable them.
The following pages (4) detail the new security procedures and specify how to construct remote script URLs to enable them to be used repeatedly upon an initial user-approval process.
NOTE: The new security protocols do not effect Omni Automation plug-ins installed by users.
Background
Omni Automation is based upon the JavaScriptCore frameworks that are included with all Apple devices and platforms (iOS, iPadOS, macOS). The JavaScript scripting language component of these frameworks is an essential element in the construction of the webpages that make up the World-Wide-Web, and is also commonly used to automate the processes of many 3rd-party applications.
Because of this shared JavaScript “heritage,” 3rd-party applications and webpages are able to create and execute Omni Automation script URLs containing JavaScript script code and passed data, enabling direct interaction with the full suite of Omni applications.
These “external” scripts may be static (the same code executing the same each time) or they may incorporate a design that enables data to be processed dynamically by the targeted Omni application, with the processed data changing each time the script is run.
Regardless of the script’s design, Omni Automation scripts are “sent” to the targeted Omni application via URLs that contain the encoded Omni Automation script code, and optionally any unique data to processed by the script.
This section of the website details how to use standard JavaScript code in apps and webpages to create encoded Omni Automation script URLs.
The Script URL Syntax
Omni Automation script URLs have a simple design and implementation, that includes the delivery and execution of customized Omni Automation scripts, without requiring the complex chains of parameter=value pairs commonly used in other URL automation scenarios.
Omni Automation script URLs incorporate two parameters, the second of which is optional:
- The “script” parameter is followed by an equal sign (=) and the percent-encoded Omni Automation script to be executed by the targeted Omni application.
- The optional “arg” parameter is appended to the script URL and is preceded with an ampersand (&) and followed by an equal sign (=) and the percent-encoded data to be used as input for the script, often called the script: “argument”
Here is the Omni Automation script URL format, beginning with the lowercase name of the targeted Omni application:
[Omni app name]://localhost/omnijs-run?script=[%-encoded script]&arg=[%-encoded argument]
Script Design
For external scripts that include the processing script code as well as the data to be processed by the script, there are essentially two designs that work well:
- The script is comprised of one or more script statements that incorporate the use of the term “argument” as a placeholder for the passed data to be processed.
- The script is written as a self-invoking function that accepts input to be processed by the function.
Script as Statement(s)
argument.forEach(arg => {console.log(arg)})
Script as Function
(function logTheseItems(args){
args.forEach(arg => {console.log(arg)})
})(argument)
NOTE: The optional script argument can be provided as a string, array, or object.
Argument (optional)
// string argument
"How now brown cow."
// array argument
["East", "South", "North", "West"]
// object argument
{"name":"My New Project", "notification":-3600, "dueDate":"Mon Apr 19 2021 09:30:00 GMT-0700 (PDT)"}
URL from Script Statement(s)
The following example demonstrates how to use JavaScript to create an Omni Automation script URL from a script and data.
In this simple example, the Omni Automation script will iterate an array of passed strings, logging each to the automation console in the target Omni application.
NOTE: prior to executing the example script below, you will want to become familiar with the built-in Omni Automation script security protocols.
Script with Passed Input
scriptString = `argument.forEach(arg => {console.log(arg)})`
scriptInput = ["A","B","C"]
encodedScript = encodeURIComponent(scriptString)
inputString = JSON.stringify(scriptInput)
encodedInput = encodeURIComponent(inputString)
targetAppName = "OmniFocus".toLowerCase()
urlString = `${targetAppName}://localhost/omnijs-run?script=${encodedScript}&arg=${encodedInput}`
For this example, the resulting script URL looks similar to:
Encoded Script URL
omnifocus://localhost/omnijs-run?script=argument.forEach(arg%20%3D%3E%20%7Bconsole.log(arg)%7D)&arg=%5B%22A%22%2C%22B%22%2C%22C%22%5D
URL from Script Function
The following example demonstrates how to use JavaScript to create an Omni Automation script URL when the source script is written as a single function that accepts and processes passed-in data.
NOTE: prior to executing the example script below, you will want to become familiar with the built-in Omni Automation script security protocols.
Script as Function
function logTheseItems(args){
args.forEach(arg => {
console.log(arg)
})
}
functionInput = ["A","B","C"]
functionString = logTheseItems.toString()
encodedFunction = encodeURIComponent(functionString)
inputString = JSON.stringify(functionInput)
encodedInput = encodeURIComponent(inputString)
targetAppName = "OmniFocus".toLowerCase()
op = "%28" // open paren
cp = "%29" // close paren
urlString = `${targetAppName}://localhost/omnijs-run?script=${op}${encodedFunction}${cp}${op}argument${cp}&arg=${encodedInput}`
For this example, the resulting script URL looks similar to:
omnifocus://localhost/omnijs-run?script=%28function%20logTheseItems(args)%7B%0A%09args.forEach(arg%20%3D%3E%20%7B%0A%09%09console.log(arg)%0A%09%7D)%0A%7D%29%28argument%29&arg=%5B%22A%22%2C%22B%22%2C%22C%22%5D
Executing the Script URL
The JavaScript code for executing (opening) the script URL depends upon the host application or webpage.
For example, the Drafts application uses the openURL() function in its JavaScript implementation to open (execute) URLs.
NOTE: In Drafts, the individual automation scripts (plug-ins) for controlling Drafts are called “actions.”
The following example Drafts “action” is designed to copy the content of the current draft to the selected OmniGraffle solid graphic. It uses the openURL() function to execute the generated Omni Automation script URL:
Drafts Action: Copy Document to Selected Graphic
(() => {
// wrapping the Drafts action script in a self-invoking anonymous arrow function (() => {})();
// prevents possible conflicts between Drafts script actions that may use the same variable names
// OmniGraffle JavaScript Context
// Omni Automation script as a function with text input
function setTextForSelectedSolid(textInput){
if (document.windows[0].selection.solids.length != 1){
title = "SELECTION ERROR";
message = "Please select a single graphic.";
new Alert(title, message).show();
} else {
cnvs = document.windows[0].selection.canvas;
graphic = document.windows[0].selection.solids[0];
currentTextSize = graphic.textSize;
graphic.text = textInput;
graphic.textSize = currentTextSize;
}
};
// Host Application JavaScript Context (1Writer, Drafts, etc.)
const docContent = draft.content;
const contentString = JSON.stringify(docContent);
const encodedContent = encodeURIComponent(contentString);
const functionString = setTextForSelectedSolid.toString();
const encodedFunction = encodeURIComponent(functionString);
// construct and execute an “approvable” Omni Automation script URL
app.openURL(
'omnigraffle://localhost/omnijs-run?script=' +
'%28' + encodedFunction + '%29' +
'%28' + 'argument' + '%29' +
'&arg=' + encodedContent
);
})();
For those instances where the Omni Automation script is generated by a webpage, the page’s main JavaScript script would change the value of the location property of the Window class as a way to execute the Omni Automation script URL:
Executing Script URL from Webpage
window.location = scriptURL;
The creation and execution of Omni Automation URLs by webpages is throughly detailed here.
Security
Regardless of the mechanism used to execute the Omni Automation script URL, you need to be aware of script security procedures used by all Omni applications, whether hosted on iOS, iPadOS, or macOS. These are covered in detail in the next page of this section.