Omni App-to-App Actions
The strength of Omni’s productivity suite of applications becomes apparent when they interact with each other through the use of Omni Automation. Imagine these scenarios, where through the use of scripts you can:
Transfer data from one Omni application to another for processing in the targeted application. For example, creating a new outline in OmniOutliner populated with data about the selected OmniFocus tasks.
Extract data from a targeted Omni application for use in the originating Omni application. For example, using the data in the current OmniOutliner document to create a table in OmniGraffle.
Transfer data from an Omni application to another Omni application for processing in the targeted application, then returning data to the originating application for processing in the originating application. For example, exporting the tasks of an OmniPlan project to OmniFocus and inserting links in the OmniPlan to their corresponding tasks created in OmniFocus.
The following documentation details the use of the Omni Automation functions that enable direct communication between Omni applications. Materials regarding the interaction of Omni application with 3rd-party applications is documented elsewhere on this site.
Script Encoding Functions
Omni Automation uses script URLs to send Omni Automation scripts between its applications and from web-based documentation to its applications. As an example, this website often presents links and buttons that when triggered will execute Omni Automation example scripts on your behalf.
Omni app-to-app scripts incorporate the use of specialized functions that are used to convert a JavaScript function into an Omni Automation script URL that when called will pass the function to the targeted Omni application for execution.
Here are the two functions of the URL class designed to be used for Omni app-to-app communication:
tellScript(app:String, script:String, arg:Object or null) → (URL or null) • Creates a percent-encoded script URL to invoke the given Omni Automation script on the targeted Omni application. The result of this function is appropriate for use with the call() function of the URL class. IMPORTANT: this function has been superseded by the tellFunction() function, which should be used instead.
tellFunction(app:String, function name:String, arg:Object or null) → (URL or null) • Creates a percent-encoded script URL to invoke the given Omni Automation JavaScript function on the targeted Omni application. The result of this function is appropriate for use with the call() function of the URL class.
Here is an example of the use of the tellFunction() function:
Omni Automation Script URL | ||
01 | function targetAppFunction(){console.log("test");return "completed"} | |
02 | var targetAppName = "omnigraffle" | |
03 | var scriptURL = URL.tellFunction(targetAppName, targetAppFunction) | |
04 | console.log(scriptURL) | |
05 | //--> [object URL: omnigraffle://localhost/omnijs-run?script=(function%20targetAppFunction()%7Bconsole.log(%22test%22);return%20%22completed%22%7D)();] |
The call() Function
Once the script URL has been generated by the tellFunction() function, the call() function is used to send the script to the targeted Omni application for execution. In addition, the call() function adapts the script URL to include an x-callback-url that returns the script result (or error) to the originating application for processing.
call(success:Function, failure:Function or null) → (passed script result/object or error) • Invoke an x-callback-url API end-point, with the callback functions being invoked when a reply is received. When a reply is received, the parameters of that URL are decoded as JSON, or left as String values if not valid JSON, and stored as properties of a result object. For a successful reply, if the result object has one property, its value is passed as the first argument to the success function. If there are zero or more than one parameters, the full object is passed as the first argument. In both cases, the success function is passed a second argument that is the full object of parameters. The failure callback is always passed the object will all the result parameters, typically errorCode and errorMessage.
Here is an example of using the call() function without the optional error handling:
call() Function with Result | ||
01 | function targetAppFunction(){console.log("test");return "completed"} | |
02 | var targetAppName = "omnigraffle" | |
03 | var scriptURL = URL.tellFunction(targetAppName, targetAppFunction) | |
04 | scriptURL.call(function(reply){ | |
05 | // PROCESS RESULTS OF SCRIPT | |
06 | if (typeof reply === "object"){ | |
07 | reply = reply["result"] | |
08 | } | |
09 | console.log(reply) | |
10 | }) |
call() Function with Result and Error Handlers | ||
01 | function targetAppFunction(){console.log("test");return "completed"} | |
02 | var targetAppName = "omnigraffle" | |
03 | var scriptURL = URL.tellFunction(targetAppName, targetAppFunction) | |
04 | scriptURL.call(function(reply){ | |
05 | // PROCESS RESULTS OF SCRIPT | |
06 | if (typeof reply === "object"){reply = reply["result"]} | |
07 | console.log(reply) | |
08 | }, function(err){ | |
09 | // PROCESS SCRIPT ERROR | |
10 | new Alert("SCRIPT ERROR", err.errorMessage).show() | |
11 | console.error(err) | |
12 | }) |
The tellFunction() Function
The tellFunction() function of the URL class provides a very efficient mechanism for performing actions in another Omni application, and optionally returning the results of those actions back to the Omni application originating the Omni Automation app-to-app script.
To use this function, you include a JavaScript function for the targeted Omni application, in the Omni Automation action script for the Omni application hosting the action. The tellFunction() function in your hosted action will automatically convert the included target function into a percent-encoded string that will be included in a script URL executed by the call() function of the URL class. The encoded script will be translated and the passed function executed by the targeted Omni application. Optionally, any results of the function can be returned to the originating application for further use by the hosted script.
In addition, the passed function has the option to receive input through the inclusion of a single passed-in argument that may be either a string, number, date, array, or object. The only requirement for the passed argument is that it be compatible with the JSON.stringify() function, which is how the passed function arguments are made ready for inclusion in the script URL generated by the tellFunction() method.
In the following example action, which targets the OmniOutliner application from any of the other Omni apps, the tellFunction() function is used to pass a function (that will be executed in OmniOutliner) that has no input argument. Note that the result of the passed function (line 20: in this example, a simple message) is returned to the originating script for processing.
In the example, the passed function simply logs a message in the targeted application’s console and returns a message to the originating script, that logs the returned message in the originating app’s console.
Note that the passed function in the example action incorporates the use of the try, catch, and throw statements in order to return an error message to the originating application. Any error message is processed by the second call-back function parameter in the call() function.
App-to-App Function with No Passed Arguments | ||
01 | /*{ | |
02 | "type": "action", | |
03 | "targets": ["omnigraffle","omnifocus","omniplan"], | |
04 | "author": "Otto Automator", | |
05 | "identifier": "com.omni-automation.ex.tellfunction", | |
06 | "version": "1.1", | |
07 | "description": "A testing script for the tellFunction() function of the URL class", | |
08 | "label": "tellFunction()", | |
09 | "shortLabel": "tellFunction()" | |
10 | }*/ | |
11 | (() => { | |
12 | var action = new PlugIn.Action(function(selection, sender){ | |
13 | // action code | |
14 | ||
15 | var targetAppName = "omnioutliner" | |
16 | ||
17 | // THE FUNCTION TO BE EXECUTED ON THE TARGET APP | |
18 | function targetAppFunction(){ | |
19 | try { | |
20 | // SCRIPT CODE TO EXECUTE | |
21 | ||
22 | console.log("Passed function has been executed") | |
23 | return "completed" | |
24 | } | |
25 | catch(error){ | |
26 | console.error("An error occurred.") | |
27 | throw error | |
28 | } | |
29 | } | |
30 | ||
31 | // CREATE SCRIPT URL WITH FUNCTION | |
32 | var scriptURL = URL.tellFunction( | |
33 | targetAppName, | |
34 | targetAppFunction, | |
35 | null | |
36 | ) | |
37 | ||
38 | // CALL THE SCRIPT URL, PROCESS RESULTS OR ERROR | |
39 | scriptURL.call(function(reply){ | |
40 | // PROCESS RESULTS OF SCRIPT | |
41 | if (typeof reply === "object"){ | |
42 | reply = reply["result"] | |
43 | } | |
44 | console.log(reply) | |
45 | }, function(err){ | |
46 | // PROCESS SCRIPT ERROR | |
47 | new Alert("SCRIPT ERROR", err.errorMessage).show() | |
48 | console.error(err) | |
49 | }) | |
50 | ||
51 | action.validate = function(selection, sender){ | |
52 | // validation code | |
53 | return true | |
54 | }; | |
55 | ||
56 | return action; | |
57 | })(); |
The tellFunction() Function: Object Argument
With the tellFunction() method, the passed function has the option to receive input (before it is passed) through the inclusion of a single passed-in argument that may be either a string, number, date, array, or object. The only requirement for the passed argument is that it be compatible with the JSON.stringify() function, which is how the passed function arguments are made ready for inclusion in the script URL generated by the tellFunction() method.
In many actions implementing a passed data argument, the data is generated dynamically by the script and may represent the contents of objects and elements in the document of the host application.
In the following example, the argument for the passed function is a JavaScript object containing a series of key:value data pairs, with the values being: a string, a number, and an array of strings (lines 18—23). The created object is then added as the last parameter of the tellFunction() function. (line 39)
Note that the passed function in the example action incorporates the use of the try, catch, and throw statements in order to return an error message to the originating application. Any error message is processed by the second call-back function parameter in the call() function.
App-to-App Function with Object Argument | ||
01 | /*{ | |
02 | "type": "action", | |
03 | "targets": ["omnigraffle","omnifocus","omniplan"], | |
04 | "author": "Otto Automator", | |
05 | "identifier": "com.omni-automation.ex.tellfunction-object", | |
06 | "version": "1.1", | |
07 | "description": "A testing script for the tellFunction() function of the URL class", | |
08 | "label": "tellFunction(object)", | |
09 | "shortLabel": "tellFunction(object)" | |
10 | }*/ | |
11 | (() => { | |
12 | var action = new PlugIn.Action(function(selection, sender){ | |
13 | // action code | |
14 | ||
15 | var targetAppName = "omnioutliner" | |
16 | ||
17 | // ARGUMENT MAY BE: string, number, date, array, or object | |
18 | var targetFunctionArgument = { | |
19 | "stringValueKey": "HOW NOW BROWN COW", | |
20 | "numericValueKey": 1234, | |
21 | "arrayValueKey": ["Sal","Sue","Carl","Wanda"], | |
22 | "dateValueKey": new Date() | |
23 | } | |
24 | ||
25 | // THE FUNCTION TO BE EXECUTED ON THE TARGET APP | |
26 | function targetAppFunction(arg){ | |
27 | try { | |
28 | var stringValue = arg["stringValueKey"] | |
29 | console.log(stringValue) | |
30 | var numericValue = arg["numericValueKey"] | |
31 | console.log(numericValue) | |
32 | var arrayValue = arg["arrayValueKey"] | |
33 | arrayValue.forEach(item => { | |
34 | console.log(item) | |
35 | }) | |
36 | var dateValueString = arg["dateValueKey"] | |
37 | console.log(Date(dateValueString)) | |
38 | ||
39 | return "completed" | |
40 | } | |
41 | catch(error){ | |
42 | console.error("An error occurred.") | |
43 | throw error | |
44 | } | |
45 | } | |
46 | ||
47 | // CREATE SCRIPT URL WITH FUNCTION | |
48 | var scriptURL = URL.tellFunction( | |
49 | targetAppName, | |
50 | targetAppFunction, | |
51 | targetFunctionArgument | |
52 | ) | |
53 | ||
54 | // CALL THE SCRIPT URL, PROCESS RESULTS OR ERROR | |
55 | scriptURL.call(function(reply){ | |
56 | // PROCESS RESULTS OF SCRIPT | |
57 | if (typeof reply === "object"){ | |
58 | reply = reply["result"] | |
59 | } | |
60 | console.log(reply) | |
61 | }, function(err){ | |
62 | // PROCESS SCRIPT ERROR | |
63 | new Alert("SCRIPT ERROR", err.errorMessage).show() | |
64 | console.error(err) | |
65 | }) | |
66 | ||
67 | action.validate = function(selection, sender){ | |
68 | // validation code | |
69 | return true | |
70 | }; | |
71 | ||
72 | return action; | |
73 | })(); |
Copy Selected OmniFocus Tasks to OmniPlan
In the following example, data is gathered about the selected OmniFocus tasks and then set to OmniPlan to recreate the tasks in the current project. In this case, the argument for the passed function is a JavaScript array of JavaScript objects, with each object containing the specifics of a selected OmniFocus task. The transfered data is then processed in sequence to construct new tasks in OmniPlan.
(Requires OmniFocus 3.7 or newer)
Example Argument Data | ||
01 | [{"OFtaskTitle":"TASK ONE","OFtaskNote":"omnifocus:///task/gRAODj8WNwd","OFtaskDueDate":"2020-03-26T00:00:00.000Z"},{"OFtaskTitle":"TASK TWO","OFtaskNote":"omnifocus:///task/iakN8cX6A5u","OFtaskDueDate":"2020-03-16T07:00:00.000Z"},{"OFtaskTitle":"TASK THREE","OFtaskNote":"omnifocus:///task/hKtVOc-4SXV","OFtaskDueDate":"2020-03-16T07:00:00.000Z"}] |
Here is the example plug-in:
Copy Selected OmniFocus Tasks to OmniPlan | ||
01 | /*{ | |
02 | "type": "action", | |
03 | "targets": ["omnifocus"], | |
04 | "author": "Otto Automator", | |
05 | "identifier": "com.omni-automation.of.op.copy-tasks-to-omniplan", | |
06 | "version": "1.0", | |
07 | "description": "This action will create copies of the selected tasks in the current OmniPlan project.", | |
08 | "label": "Copy Selected Tasks to OmniPlan", | |
09 | "shortLabel": "Copy to OmniPlan" | |
10 | }*/ | |
11 | (() => { | |
12 | var action = new PlugIn.Action(function(selection, sender){ | |
13 | // action code | |
14 | // selection options: tasks, projects, folders, tags | |
15 | ||
16 | var now = new Date() | |
17 | var today = Calendar.current.startOfDay(now) | |
18 | ||
19 | var targetFunctionArgument = new Array() | |
20 | selection.tasks.forEach(function(OFtask){ | |
21 | var OFtaskTitle = OFtask.name | |
22 | var OFtaskID = OFtask.id.primaryKey | |
23 | var OFtaskLink = "omnifocus:///task/" + OFtaskID | |
24 | var OFtaskNote = OFtask.note | |
25 | if (OFtaskNote){ | |
26 | OFtaskNote = (OFtaskNote.length > 0) ? OFtaskNote + "\n" + OFtaskLink : OFtaskLink | |
27 | } else { | |
28 | OFtaskNote = OFtaskLink | |
29 | } | |
30 | var OFtaskDueDate = OFtask.dueDate | |
31 | OFtaskDueDate = (OFtaskDueDate != null) ? OFtaskDueDate : today | |
32 | // CREATE TASK DATA OBJECT | |
33 | var taskDataObj = { | |
34 | "OFtaskTitle":OFtaskTitle, | |
35 | "OFtaskNote":OFtaskNote, | |
36 | "OFtaskDueDate":OFtaskDueDate | |
37 | } | |
38 | targetFunctionArgument.push(taskDataObj) | |
39 | }) | |
40 | ||
41 | console.log(JSON.stringify(targetFunctionArgument)) | |
42 | ||
43 | function targetAppFunction(arg){ | |
44 | try { | |
45 | var OPTaskLinks = new Array() | |
46 | arg.forEach(taskDataObj => { | |
47 | var OPtask = actual.rootTask.addSubtask() | |
48 | var OPtaskLink = "omniplan:///task/" + OPtask.uniqueID | |
49 | OPtask.title = taskDataObj["OFtaskTitle"] | |
50 | OPtask.note = taskDataObj["OFtaskNote"] | |
51 | OPtask.endNoLaterThanDate = new Date(taskDataObj["OFtaskDueDate"]) | |
52 | OPTaskLinks.push(OPtaskLink) | |
53 | }) | |
54 | return OPTaskLinks | |
55 | } | |
56 | catch(error){ | |
57 | console.log("An error occurred.") | |
58 | throw error | |
59 | } | |
60 | } | |
61 | ||
62 | var scriptURL = URL.tellFunction( | |
63 | "omniplan", | |
64 | targetAppFunction, | |
65 | targetFunctionArgument | |
66 | ) | |
67 | ||
68 | scriptURL.call(function(result){ | |
69 | // PROCESS RESULTS OF SCRIPT | |
70 | console.log("targetAppFunction result: ", result) | |
71 | var selectedOFTasks = selection.tasks | |
72 | result.forEach((OPTaskLink, index) => { | |
73 | var OFTask = selectedOFTasks[index] | |
74 | var OFTaskNote = OFTask.note | |
75 | if (OFTaskNote){ | |
76 | if(OFTaskNote.length > 0){ | |
77 | OFTask.note = OFTaskNote + "\n" + OPTaskLink | |
78 | } else { | |
79 | OFTask.note = OPTaskLink | |
80 | } | |
81 | } else { | |
82 | OFTask.note = OPTaskLink | |
83 | } | |
84 | }) | |
85 | }, function(error){ | |
86 | // PROCESS SCRIPT ERROR | |
87 | new Alert("SCRIPT ERROR", error).show() | |
88 | console.error(error) | |
89 | }) | |
90 | ||
91 | }); | |
92 | ||
93 | action.validate = function(selection, sender){ | |
94 | // validation code | |
95 | // selection options: tasks, projects, folders, tags | |
96 | return (selection.tasks.length > 0) | |
97 | }; | |
98 | ||
99 | return action; | |
100 | })(); |
This webpage is in the process of being developed. Any content may change and may not be accurate or complete at this time.