New Outline with Graphic Metadata

The workflow example on this page contains two Omni Automation actions that demonstrate how data can be extracted from a document in one application, and used to construct or add to a document in a second application.

The first action extracts the titles of the metadata keys of a selected OmniGraffle graphic, and then uses those key titles to add columns for each key to a newly created OmniOutliner outline document.

The second action populates the new OmniOutliner document with the metadata for all of the selected graphics in the OmniGraffle document.

The initial section of the first action begins with a function for getting the assigned metadata keys for the selected graphic. These keys will be used for creating and titling columns in a new OmniOutliner document.

function getKeysForSelectedGraphic(){ var graphics = document.windows[0].selection.graphics if (graphics.length === 1){ data = graphics[0].userData return Object.keys(data) } else { var title = 'SELECTION ERROR' var message = 'Please select a single graphic.' var alert = new Alert(title, message) alert.show() } } var keys = getKeysForSelectedGraphic()

The second section of the first action contains code for interacting with OmniOutliner. A new document is created and columns are added and titled for each of the extracted metadata keys.

function makeNewOutlineWithColumns(colTitles){ Document.makeNewAndShow(doc => { var tree = doc.outline var editor = doc.editors[0] colTitles.forEach(item => { tree.addColumn( Column.Type.Text, editor.afterColumn(tree.columns[tree.columns.length -1]), column => {column.title = item} ) }) }) }

Here's how the function would be called, passing in an array of column titles:

var titles = ["Q1", "Q2", "Q3", "Q4"] makeNewOutline(titles)
function makeNewOutlineWithColumns(columnTitles){ Document.makeNewAndShow(doc => { var tree = doc.outline var editor = doc.editors[0] columnTitles.forEach(item => { tree.addColumn( Column.Type.Text, editor.afterColumn(tree.columns[tree.columns.length -1]), column => {column.title = item} ) }) }) } var columnTitles = ["Q1", "Q2", "Q3", "Q4"] makeNewOutlineWithColumns(columnTitles)
omnigraffle://localhost/omnijs-run?script=try%7Bfunction%20makeNewOutline%28colTitles%29%7B%0A%09Document%2EmakeNewAndShow%28doc%20%3D%3E%20%7B%0A%09%09var%20tree%20%3D%20doc%2Eoutline%0A%09%09var%20editor%20%3D%20doc%2Eeditors%5B0%5D%0A%09%09colTitles%2EforEach%28item%20%3D%3E%20%7B%0A%09%09%09tree%2EaddColumn%28%0A%09%09%09%09Column%2EType%2EText%2C%0A%09%09%09%09editor%2EafterColumn%28tree%2Ecolumns%5Btree%2Ecolumns%2Elength%20%2D1%5D%29%2C%20%0A%09%09%09%09column%20%3D%3E%20%7Bcolumn%2Etitle%20%3D%20item%7D%0A%09%09%09%29%0A%09%09%7D%29%0A%09%7D%29%0A%7D%0Avar%20titles%20%3D%20%5B%22Q1%22%2C%20%22Q2%22%2C%20%22Q3%22%2C%20%22Q4%22%5D%0AmakeNewOutline%28titles%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D

Example Plug-In

DOWNLOAD the example document shown in the video.

With the script-to-string preparation completed, we can use the script components to crate an installable Omni Automation action that can be launched from the OmniGraffle Automation menu.

/*{ "type": "action", "targets": ["omnigraffle"], "author": "Otto Automator", "identifier": "com.omni-automation.og.outline-with-columns-for-keys", "version": "1.0", "description": "Creates a new OmniOutliner outline document with columns for each metadata tag of the selected graphic.", "label": "Outline with Columns for Keys", "shortLabel": "Outline with Columns", "paletteLabel": "New Outline" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // action code // GET METADATA KEYS OF SELECTED GRAPHIC var graphic = selection.graphics[0] var data = graphic.userData var columnTitles = Object.keys(data) console.log(columnTitles) // THE FUNCTION TO BE EXECUTED ON THE TARGET APP function makeNewOutlineWithColumns(titlesArray){ try { Document.makeNewAndShow(doc => { var tree = doc.outline var editor = doc.editors[0] titlesArray.forEach(item => { tree.addColumn( Column.Type.Text, editor.afterColumn(tree.columns[tree.columns.length -1]), column => {column.title = item} ) }) }) } catch(error){ console.error("An error occurred.") throw error } } // CREATE SCRIPT URL WITH FUNCTION var scriptURL = URL.tellFunction( "omnioutliner", makeNewOutlineWithColumns, columnTitles ) // CALL THE SCRIPT URL, PROCESS RESULTS OR ERROR scriptURL.call(callResult => { // PROCESS RESULTS OF SCRIPT if (typeof callResult === "object"){ var callResult = callResult["result"] } console.log(callResult) }, function(err){ // PROCESS SCRIPT ERROR new Alert("SCRIPT ERROR", err.errorMessage).show() console.error(err) }) }); action.validate = function(selection, sender){ // validation code return (selection.graphics.length === 1) }; return action; })();

 64-67  The validation function checks to see that a single graphic is selected in the OmniGraffle document. If a single graphic is selected, the conditional statement returns a value of true, otherwise returns a value of false.

 18  The metadata key/value pairs for selected graphic are retrieved from the userData property. The result will be an array of JavaScript objects.

 19  An array of metadata key titles is extracted from the metadata object pairs using the keys() method of the Object class.

 23-41  The function to send to OmniOutliner to be executed. This function will create a new outline document with columns for each of the metadata keys of the selected graphic.

 44-48  The tellFunction() method of the URL class is used to generate the encoded script url containing the executing function and the array of key titles to be passed into it as input. Line 47 is the arguments to passed into the function — in this example it’s an array of key titles.

 51-62  The call() method of the URL class is used to execute the generated script url and return any results or error to the plug-in in OmniGraffle.

Now that we have a script for generating a new OmniOutliner document with columns matching the metadata to the OmniGraffle graphic, we can create a script for populating the new outline with the metadata from multiple selected OmniGraffle graphics!

Populating Outline with Metadata

The script for populating the newly created OmniOutliner outline document with metadata from the selected OmniGraffle graphics begins with script code for extracting the metadata assigned to each selected graphic.

NOTE: the following scripts assume that all of the selected graphics have the same metadata keys, and that those metadata keys match the columns in the target OmniOutliner outline document.

graphics = document.windows[0].selection.graphics dataArray = [] graphics.forEach(function(graphic){ data = graphic.userData data['Name'] = graphic.name data['Notes'] = graphic.notes dataArray.push(data) }) dataString = JSON.stringify(dataArray)

 1  Generate an array of references to the selected graphics.

 2  Declare an empty array that will hold the extracted metadata objects.

 3-8  Process each selected graphic using the forEach() method.

 4  Use the userData property to extract the metadata of the graphic, which will be a JavaScript object containing the key:value metadata pairs.

 5  Add a metadata pair for the graphic name.

 6  Add a metadata pair for the graphic’s notes.

 7  Add the appended data object to the array.

 9  Use the stringify() method to convert the array of objects to a string

 10  The resulting string.

The second part of the script populates the outline document by creating a new row for each object, and then putting the keyed metadata in the corresponding row column.

dataArray.forEach(obj => { var item = rootItem.addChild() tags = Object.keys(obj) tags.forEach(tag => { if(tag === "Name"){ item.topic = obj.Name } else if(tag === "Notes"){ item.note = obj.Notes } else { item.setValueForColumn(obj[tag],columns.byTitle(tag)) } }) })

 1-13  Iterate the array of data objects using a forEach() method.

 2  Add a new top-level row by using the addChild() method with the rootItem.

 3  Get an array of the object keys.

 4-12  Iterate the extracted keys using a forEach() method.

 5-6  If the key is “Name” then set the topic of the row to the value of the object’s “Name” pair.

 7-8  If the key is “Notes” then set the note of the row to the value of the object’s “Notes” pair.

 10  Using the tag, identify the corresponding column, and insert the data pair’s value in the corresponding row cell.

Using the same techniques as in the previous action, these script segments can be wrapped in an action template to create and installable action that can be accessed from the OmniGraffle Automation menu.

/*{ "type": "action", "targets": ["omnigraffle"], "author": "Otto Automator", "identifier": "com.omni-automation.og.populate-outline-with-metadata", "version": "1.0", "description": "Creates a new OmniOutliner outline document with columns for each metadata tag of the selected graphic.", "label": "Populate Outline with Metadata", "shortLabel": "Metadata to Outline", "paletteLabel": "Populate Outline" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // action code // ADD TO METADATA OBJECTS OF SELECTED GRAPHICS var objectArray = [] selection.graphics.forEach(graphic => { var data = graphic.userData data['Name'] = graphic.name data['Notes'] = graphic.notes objectArray.push(data) }) // THE FUNCTION TO BE EXECUTED ON THE TARGET APP function populateOutline(dataArray){ try { dataArray.forEach(obj => { var item = rootItem.addChild() tags = Object.keys(obj) tags.forEach(tag => { if(tag === "Name"){ item.topic = obj.Name } else if(tag === "Notes"){ item.note = obj.Notes } else { item.setValueForColumn(obj[tag],columns.byTitle(tag)) } }) }) return true } catch(error){ console.error("An error occurred.") throw error } } // CREATE SCRIPT URL WITH FUNCTION var scriptURL = URL.tellFunction( "omnioutliner", populateOutline, objectArray ) // CALL THE SCRIPT URL, PROCESS RESULTS OR ERROR scriptURL.call(callResult => { // PROCESS RESULTS OF SCRIPT if (typeof callResult === "object"){ var callResult = callResult["result"] } console.log(callResult) if(callResult === true){URL.fromString("omnioutliner:///open?").open()} }, function(err){ // PROCESS SCRIPT ERROR new Alert("SCRIPT ERROR", err.errorMessage).show() console.error(err) }) }); action.validate = function(selection, sender){ // validation code return (selection.graphics.length > 0) }; return action; })();
UNDER CONSTRUCTION

This webpage is in the process of being developed. Any content may change and may not be accurate or complete at this time.

DISCLAIMER