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 { alert = new Alert('SELECTION ERROR','Please select a single graphic.') alert.show(function(result){}) throw new Error('user selection') } } 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 makeNewOutline(){ Document.makeNewAndShow(function(doc){ tree = doc.outline editor = doc.editors[0] keys = ["key 1","key 2","key 3","etc."].sort() keys.forEach(function(key){ tree.addColumn( Column.Type.Text, editor.afterColumn(tree.columns.length -1), function(column){column.title = key} ) }) }) }

In Omni Automation, scripts can be run in an application that contain script code that targets a second application. In order to execute the script code in the target application, the script code that is to be used to target the second application must first be converted into a string, and then encoded using the tellScript() method of the URL class.

For JavaScript code expressed as a function, the conversion to string can be done easily by creating a statement that contains the function name followed by the toString() method:

Use of the toString() method is appropriate if the JavaScript code to be converted into a string is a function. However, in this example, data gathered from the graphics in OmniGraffle will need to be inserted into the JavaScript code that will be executed in OmniOutliner. This requires that the function be expressed as a string that can be edited to receive the data generated by the OmniGraffle part of the script.

So, to be converted from a standard JavaScript code block into a single-line string, the statements in the example JavaScript script code must be delineated with semi-colon characters. This will allow the script to be written as a single line of JavaScript code statements, ready to be convert into a string. Here is a link on the w3schools.com website about using semi-colons to delineate JavaScript statements.

NOTE: In Omni Automation, the use of semi-colons as statement delimiters is usually optional, except when creating app-to-app scripts.

Once the statement delimiters have been added, the tab and paragraph return characters can be removed, leaving a single-line fully-functioning JavaScript script:

To convert the single-line JavaScript script into a string, enclose the script in two single-quote characters ('). Note that any single-quote characters used within the script will have to be escaped by preceding them with a back-slash character (\').

The final preparation step is to insert the metadata extracted by the first part of the script, into the script that will be executed in OmniOutliner. This is done using the stringify() method of the JSON class.

As shown below, the placeholder array of data is replaced with the stringify() method processing the data array generated by the first part of the script. In addition, the array of strings is sorted alphabetically using the sort() method.

The stringify() method will convert the array of metadata keys into a string that is concatenated with the enclosing parts of the previously created JavaScript code string.

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": "Nyhthawk Productions", "description": "Creates a new OmniOutliner outline document with columns for each metadata tag of the selected graphic.", "label": "New Outline with Columns for Keys", "shortLabel": "Outline for Keys" }*/ var _ = function(){ var action = new PlugIn.Action(function(selection, sender) { // action code // selection options: canvas, document, graphics, lines, solids, view data = selection.graphics[0].userData var keys = Object.keys(data) if (keys != []){ scr = 'function makeNewOutline(){Document.makeNewAndShow(function(doc){tree = doc.outline;editor = doc.editors[0];keys = ' + JSON.stringify(keys.sort()) + ';keys.forEach(function(key){tree.addColumn(Column.Type.Text,editor.afterColumn(tree.columns[tree.columns.length - 1]), function(column){column.title = key});});});};makeNewOutline()' var scriptURL = URL.tellScript("omnioutliner", scr) scriptURL.call(function(reply){}) } }); action.validate = function(selection, sender) { // validation code // selection options: canvas, document, graphics, lines, solids, view if(selection.graphics.length === 1){return true} else {return false} }; return action; }(); _;

 24-28  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.

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

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

 18  The retrieved array of key titles is converted into string format and inserted into the script that will executed in OmniOutliner to create the columns based upon the extracted metadata keys.

 19  An Omni Automation script URL targeting the OmniOutliner application is generated using tellScript() method of the URL class that receives the “stringified” script as its input.

 20  The script URL is executed, causing the script to run in OmniOutliner that uses the extracted metadata keys to create a new named column for each key.

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 script assumes 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(function(obj){ var item = rootItem.addChild(); tags = Object.keys(obj); tags.forEach(function(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": "Nyhthawk Productions", "description": "Populates an OmniOutliner document with metadata of the selected graphics.", "label": "Populate Outline with Metadata", "shortLabel": "Metadata to Outline" }*/ var _ = function(){ var action = new PlugIn.Action(function(selection, sender) { // action code // selection options: canvas, document, graphics, lines, solids, view graphics = 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) scriptString = 'dataArray = ' + dataString + ';dataArray.forEach(function(obj){var item = rootItem.addChild();tags = Object.keys(obj);tags.forEach(function(tag){if(tag === "Name"){item.topic = obj.Name;} else if(tag === "Notes"){item.note = obj.Notes;} else {item.setValueForColumn(obj[tag],columns.byTitle(tag));};});});' var scriptURL = URL.tellScript("omnioutliner", scriptString) scriptURL.call(function(reply){console.log(reply)}) }); action.validate = function(selection, sender) { // validation code // selection options: canvas, document, graphics, lines, solids, view if(selection.graphics.length > 0){return true} else {return false} }; 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