×

Tags

In OmniFocus, a tag represents an association that a task has to the world around it. A tag could represent a person, place, or thing most relevant to completion of a project, or it could represent a mindset most applicable to execution of an action item.

An item can have as many tags as you find useful, and there is no specific purpose assigned to them; use tags to assign priority, group items by energy level or required equipment, or don’t use them at all.

The Tags Perspective displays a list of your tags in the sidebar, and a list of all your actions grouped by the tags they belong to in the outline. Here is a script for setting the value of the perspective property of the Window class so that the Tags perspective is displayed:

omnifocus://localhost/omnijs-run?script=try%7Bdocument%2Ewindows%5B0%5D%2Eperspective%20%3D%20Perspective%2EBuiltIn%2ETags%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
Show Tags Perspective
 

document.windows[0].perspective = Perspective.BuiltIn.Tags
To derive an object reference to a tag by name, use the flattenedTags property of the Database class in a conditional statement that creates a new tag with a specific name if an existing tag does not exist. The result will be an object reference to the named tag:
Tag Reference by Name


var tag = flattenedTags.byName("Conifer") || new Tag("Conifer")

Class Properties of the Tag Class

Here are the properties of the Tag class:

In the View Options panel for the Forecast perspective, you can assign an existing tag to used as the “forecast tag.” Once this tag is assigned to an item, the tagged item will appear in the Forecast view. Using Omni Automation, you can add the forecast tag to a task or project.

Assign Forecast Tag to Task
 

var task = inbox.byName("Car Repair") if(task && Tag.forecastTag){task.addTag(Tag.forecastTag)}

If no tag has been assigned to be the “forecast tag” the value of this property will be returned as: null. Also note that the forecastTag property is read-only and its value cannot be set via a script.

Instance Properties of the Tag Class

Here are the properties of an instance of the Tag class:

Properties of the Tag.Status class:

A script that gets the value of the availableTasks property of the selected tag:

Available Tasks of Selected Tag


var tgs = document.windows[0].selection.tags tsks = (tgs.length === 1) ? tgs[0].availableTasks : null

A script that gets the value of the projects property of the selected tag:

Projects of Selected Tag


var tgs = document.windows[0].selection.tags projects = (tgs.length === 1) ? tgs[0].projects : null

Iterating Tags

Tag Groups are tags that contain other tags. References to top-level tags are retrieved through the use of the tags property of the Database class, as in this example for processing the top-level tags. Since the OmniFocus is the implied topmost scripting element, the values of one of its property can be accessed by simply entering the property name (such as tags) in the console:

The “tags” Database Property


tags //--> [[object Tag: Ionian],[object Tag: Phrygian],[object Tag: Lydian],[object Tag: Mixolydian],[object Tag: Aeolian],[object Tag: Locrian]]

A script example using the JavaScript forEach() function to iterate the array of top-level tags:

Iterate Top-Level Tags


tags.forEach(tag =>{ // processing statements go here })

A script example using the JavaScript map() function to retrieve the names of the top-level tags:

omnifocus://localhost/omnijs-run?script=try%7BtagNames%20%3D%20tags%2Emap%28%28tag%29%3D%3E%7Breturn%20tag%2Ename%7D%29%3Bconsole%2Elog%28tagNames%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
Names of Top-Level Tags
 

var tagNames = tags.map(tag => {return tag.name})
To return the name of all tags in the database, iterate the value of the flattenedTags property of the Database class;
Names of All Tags


var tagNames = flattenedTags.map(tag => {return tag.name})

Since the existence of multiple tags sharing the same name is allowed in OmniFocus, here’s a script that creates an instance of the Set class to remove all duplicates from the tag name list. NOTE: this does not remove duplicate tags from the database, just duplicate tag tiltes in the generated name list:

omnifocus://localhost/omnijs-run?script=try%7BtagNames%20%3D%20new%20Array%28%29%0Atags%2Eapply%28tag%20%3D%3E%20tagNames%2Epush%28tag%2Ename%29%29%0AtagNames%20%20%3D%20Array%2Efrom%28new%20Set%28tagNames%29%29%20%0Aconsole%2Elog%28tagNames%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
Get All Unique Tag Names
 

var tagNames = flattenedTags.map(tag => {return tag.name}) tagNames = Array.from(new Set(tagNames)) console.log(tagNames)

Reference Tag by Name

To provide the ability to reference a specific tag by name, the tagNamed(…) function is included with both the OmniFocus Database class and the Tag class:

Referencing Tags by Name


// Top-Level Not Existing tagNamed("Zebra") //--> null // Top-Level Exists tagNamed("Locrian") //--> [object Tag: Locrian] // 1st Tag in Database Matching Name flattenedTags.byName("Locrian") //--> [object Tag: Locrian] // Tags within Tag Group tagNamed("Locrian").children //--> [[object Tag: Minor],[object Tag: Major]] // Tag within Tag Group tagNamed("Locrian").tagNamed("Major") //--> [object Tag: Major] // 1st Tag within Tag Group tagNamed("Locrian").flattenedTags.byName("Major")

Create New Tag

Instances of the Tag class are created using the standard JavaScript new item constructor.

omnifocus://localhost/omnijs-run?script=try%7Btag%20%3D%20new%20Tag%28%22LAST%20BY%20DEFAULT%22%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
New Top-Level Tag at End of Tags
 

tag = new Tag("LAST BY DEFAULT")
omnifocus://localhost/omnijs-run?script=try%7Btag%20%3D%20new%20Tag%28%22FIRST%20TAG%22%2C%20tags%2Ebeginning%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
New Top-Level Tag at Beginning of Tags
 

tag = new Tag("FIRST TAG", tags.beginning)

Using a JavaScript logical operator (||) to reference a top-level tag by name, or creating it if it does not exist. As explanation, A || B returns the value A if A can be coerced into true; otherwise, it returns B. Therefore, in the example below, if the tag “ZEBRA” exists, a reference to it will be returned, otherwise a new tag named “ZEBRA” will be created and a reference to the created tag will be the result that is stored in the variable.

omnifocus://localhost/omnijs-run?script=try%7Btag%20%3D%20tagNamed%28%22ZEBRA%22%29%20%7C%7C%20new%20Tag%28%22ZEBRA%22%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
Reference to Tag Either Existing or Created
 

tag = tagNamed("ZEBRA") || new Tag("ZEBRA")

And a variation of the previous script that searches the entire database for an existing tag, creating a new one if a match is not found:

Reference to Tag Either Existing or Created


tag = flattenedTags.byName("ZEBRA") || new Tag("ZEBRA")

Using the previous technique to add tags to either an existing named tag or a newly created tag with the target name:

omnifocus://localhost/omnijs-run?script=try%7BtagGroup%20%3D%20tagNamed%28%22Fall%20Colors%22%29%20%7C%7C%20new%20Tag%28%22Fall%20Colors%22%29%0Acolors%20%3D%20%5B%22Pale%20Blue%22%2C%22Harvest%20Gold%22%2C%22Autumn%20Gray%22%5D%0Acolors%2EforEach%28%28color%29%3D%3E%7B%0A%09tagGroup%2EtagNamed%28color%29%20%7C%7C%20new%20Tag%28color%2C%20tagGroup%2Ebeginning%29%0A%7D%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
Adding Tags to a Unique Top-Level Tag Group
 

tagGroup = tagNamed("Fall Colors") || new Tag("Fall Colors") colors = ["Pale Blue","Harvest Gold","Autumn Gray"] colors.forEach(color => { tagGroup.tagNamed(color) || new Tag(color, tagGroup.beginning) })

And here’s a related script that uses the moveTags() function of the Database class to sort the tags in the previously created tag group by name:

Sort Tags in Tag Group by Name


var tagGroup = tagNamed("Fall Colors") if(tagGroup){ var tgs = tagGroup.children if (tgs.length > 1){ tgs.sort((a, b) => { var x = a.name.toLowerCase(); var y = b.name.toLowerCase(); if (x < y) {return -1;} if (x > y) {return 1;} return 0; }) moveTags(tgs, tagGroup) } }

The following script demonstrates how to generate an array of tag object references to the tags corresponding to an array of tag titles:

Object References for Tags


var tagNames = ["Bow","Stern","Port","Starboard"] var tagObjects = new Array() tagNames.forEach(title => { var tag = flattenedTags.byName(title) || new Tag(title) tagObjects.push(tag) }) console.log(tagObjects)

Creating Tag Groups

A Tag Group is a tag that contains other tags. Omni Automation is useful for quickly creating one or more tag groups. In the example below, tag groups are created for regions of the United States, and the appropriate states are added to their respective region tag group.

omnifocus://localhost/omnijs-run?script=try%7Bvar%20regions%20%3D%20%5B%22Northeast%22%2C%20%22Southeast%22%2C%20%22Midwest%22%2C%20%22Southwest%22%2C%20%22West%22%5D%0A%0Avar%20stateGroups%20%3D%20%5B%5B%22Maine%22%2C%20%22Massachusetts%22%2C%20%22Rhode%20Island%22%2C%20%22Connecticut%22%2C%20%22New%20Hampshire%22%2C%20%22Vermont%22%2C%20%22New%20York%22%2C%20%22Pennsylvania%22%2C%20%22New%20Jersey%22%2C%20%22Delaware%22%2C%20%22Maryland%22%5D%2C%5B%22West%20Virginia%22%2C%20%22Virginia%22%2C%20%22Kentucky%22%2C%20%22Tennessee%22%2C%20%22North%20Carolina%22%2C%20%22South%20Carolina%22%2C%20%22Georgia%22%2C%20%22Alabama%22%2C%20%22Mississippi%22%2C%20%22Arkansas%22%2C%20%22Louisiana%22%2C%20%22Florida%22%5D%2C%5B%22Ohio%22%2C%20%22Indiana%22%2C%20%22Michigan%22%2C%20%22Illinois%22%2C%20%22Missouri%22%2C%20%22Wisconsin%22%2C%20%22Minnesota%22%2C%20%22Iowa%22%2C%20%22Kansas%22%2C%20%22Nebraska%22%2C%20%22South%20Dakota%22%2C%20%22North%20Dakota%22%5D%2C%5B%22Texas%22%2C%20%22Oklahoma%22%2C%20%22New%20Mexico%22%2C%20%22Arizona%22%5D%2C%5B%22Colorado%22%2C%20%22Wyoming%22%2C%20%22Montana%22%2C%20%22Idaho%22%2C%20%22Washington%22%2C%20%22Oregon%22%2C%20%22Utah%22%2C%20%22Nevada%22%2C%20%22California%22%2C%20%22Alaska%22%2C%20%22Hawaii%22%5D%5D%0A%0Aregions%2EforEach%28%28region%2C%20index%29%3D%3E%7B%0A%09tagGroup%20%3D%20tagNamed%28region%29%20%7C%7C%20new%20Tag%28region%29%0A%09states%20%3D%20stateGroups%5Bindex%5D%0A%09states%2EforEach%28%28state%29%3D%3E%7B%0A%09%09tagGroup%2EtagNamed%28state%29%20%7C%7C%20new%20Tag%28state%2C%20tagGroup%2Eending%29%0A%09%7D%29%0A%7D%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
Creating Tag Groups
 

var regions = ["Northeast", "Southeast", "Midwest", "Southwest", "West"] var stateGroups = [["Maine", "Massachusetts", "Rhode Island", "Connecticut", "New Hampshire", "Vermont", "New York", "Pennsylvania", "New Jersey", "Delaware", "Maryland"],["West Virginia", "Virginia", "Kentucky", "Tennessee", "North Carolina", "South Carolina", "Georgia", "Alabama", "Mississippi", "Arkansas", "Louisiana", "Florida"],["Ohio", "Indiana", "Michigan", "Illinois", "Missouri", "Wisconsin", "Minnesota", "Iowa", "Kansas", "Nebraska", "South Dakota", "North Dakota"],["Texas", "Oklahoma", "New Mexico", "Arizona"],["Colorado", "Wyoming", "Montana", "Idaho", "Washington", "Oregon", "Utah", "Nevada", "California", "Alaska", "Hawaii"]] regions.forEach((region, index)=>{ tagGroup = flattenedTags.byName(region) || new Tag(region) states = stateGroups[index] states.forEach((state)=>{ tagGroup.flattenedTags.byName(state) || new Tag(state, tagGroup.ending) }) })

Here's a variation of the previous script that sorts the tags within groups:

omnifocus://localhost/omnijs-run?script=try%7Bvar%20regions%20%3D%20%5B%22Northeast%22%2C%20%22Southeast%22%2C%20%22Midwest%22%2C%20%22Southwest%22%2C%20%22West%22%5D%0Avar%20stateGroups%20%3D%20%5B%5B%22Maine%22%2C%20%22Massachusetts%22%2C%20%22Rhode%20Island%22%2C%20%22Connecticut%22%2C%20%22New%20Hampshire%22%2C%20%22Vermont%22%2C%20%22New%20York%22%2C%20%22Pennsylvania%22%2C%20%22New%20Jersey%22%2C%20%22Delaware%22%2C%20%22Maryland%22%5D%2C%5B%22West%20Virginia%22%2C%20%22Virginia%22%2C%20%22Kentucky%22%2C%20%22Tennessee%22%2C%20%22North%20Carolina%22%2C%20%22South%20Carolina%22%2C%20%22Georgia%22%2C%20%22Alabama%22%2C%20%22Mississippi%22%2C%20%22Arkansas%22%2C%20%22Louisiana%22%2C%20%22Florida%22%5D%2C%5B%22Ohio%22%2C%20%22Indiana%22%2C%20%22Michigan%22%2C%20%22Illinois%22%2C%20%22Missouri%22%2C%20%22Wisconsin%22%2C%20%22Minnesota%22%2C%20%22Iowa%22%2C%20%22Kansas%22%2C%20%22Nebraska%22%2C%20%22South%20Dakota%22%2C%20%22North%20Dakota%22%5D%2C%5B%22Texas%22%2C%20%22Oklahoma%22%2C%20%22New%20Mexico%22%2C%20%22Arizona%22%5D%2C%5B%22Colorado%22%2C%20%22Wyoming%22%2C%20%22Montana%22%2C%20%22Idaho%22%2C%20%22Washington%22%2C%20%22Oregon%22%2C%20%22Utah%22%2C%20%22Nevada%22%2C%20%22California%22%2C%20%22Alaska%22%2C%20%22Hawaii%22%5D%5D%0Aregions%2EforEach%28%28region%2C%20index%29%3D%3E%7B%0A%09tagGroup%20%3D%20flattenedTags%2EbyName%28region%29%20%7C%7C%20new%20Tag%28region%29%0A%09states%20%3D%20stateGroups%5Bindex%5D%0A%09states%2EforEach%28%28state%29%3D%3E%7B%0A%09%09tagGroup%2EflattenedTags%2EbyName%28state%29%20%7C%7C%20new%20Tag%28state%2C%20tagGroup%2Eending%29%0A%09%7D%29%0A%09var%20tgs%20%3D%20tagGroup%2Echildren%0A%09if%20%28tgs%2Elength%20%3E%201%29%7B%0A%09%09tgs%2Esort%28%28a%2C%20b%29%20%3D%3E%20%7B%0A%09%09%20%20var%20x%20%3D%20a%2Ename%2EtoLowerCase%28%29%3B%0A%09%09%20%20var%20y%20%3D%20b%2Ename%2EtoLowerCase%28%29%3B%0A%09%09%20%20if%20%28x%20%3C%20y%29%20%7Breturn%20%2D1%3B%7D%0A%09%09%20%20if%20%28x%20%3E%20y%29%20%7Breturn%201%3B%7D%0A%09%09%20%20return%200%3B%0A%09%09%7D%29%0A%09%09moveTags%28tgs%2C%20tagGroup%29%0A%09%7D%0A%7D%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
Creating Sorted Tag Groups
 

var regions = ["Northeast", "Southeast", "Midwest", "Southwest", "West"] var stateGroups = [["Maine", "Massachusetts", "Rhode Island", "Connecticut", "New Hampshire", "Vermont", "New York", "Pennsylvania", "New Jersey", "Delaware", "Maryland"],["West Virginia", "Virginia", "Kentucky", "Tennessee", "North Carolina", "South Carolina", "Georgia", "Alabama", "Mississippi", "Arkansas", "Louisiana", "Florida"],["Ohio", "Indiana", "Michigan", "Illinois", "Missouri", "Wisconsin", "Minnesota", "Iowa", "Kansas", "Nebraska", "South Dakota", "North Dakota"],["Texas", "Oklahoma", "New Mexico", "Arizona"],["Colorado", "Wyoming", "Montana", "Idaho", "Washington", "Oregon", "Utah", "Nevada", "California", "Alaska", "Hawaii"]] regions.forEach((region, index)=>{ tagGroup = flattenedTags.byName(region) || new Tag(region) states = stateGroups[index] states.forEach((state)=>{ tagGroup.flattenedTags.byName(state) || new Tag(state, tagGroup.ending) }) var tgs = tagGroup.children if (tgs.length > 1){ tgs.sort((a, b) => { var x = a.name.toLowerCase(); var y = b.name.toLowerCase(); if (x < y) {return -1;} if (x > y) {return 1;} return 0; }) moveTags(tgs, tagGroup) } })

This action plug-in will “adopt” the tags contained in tag groups that are assigned to the selected task or project:

Adopt Tags from Tag Groups
 

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of.adopt-child-tags", "version": "1.2", "description": "This action will assign the child tags of the tag groups of the selected task|project, to the selected task|project.", "label": "Adopt Tags from Tag Groups", "shortLabel": "Adopt Child Tags" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // action code if(selection.tasks[0]){ var task = selection.tasks[0] } else { var task = selection.projects[0].task } task.tags.forEach((tag)=>{ task.addTags(tag.children) }) }); action.validate = function(selection, sender){ // validation code return ( selection.tasks.length === 1 || selection.projects.length === 1 ) }; return action; })();

The following action plug-in will select all tags whose name matches the title entered in the form. If the tag is not found, an option to create and reveal it will be offered:

Tag Check
 

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of.tag-check", "description": "This action will select all of the tags sharing the provided title.", "version": "1.4", "label": "Tag Check", "shortLabel": "Tag Check" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // CONSTRUCT THE FORM var inputForm = new Form() // CREATE FORM ELEMENTS: TEXT INPUT, DATE INPUT, AND CHECKBOX textField = new Form.Field.String("tagName", null, null) // ADD THE ELEMENTS TO THE FORM inputForm.addField(textField) // DIALOG PROMPT AND OK BUTTON TITLE let formPrompt = "Enter the title of the tag to check:" let buttonTitle = "Continue" // DISPLAY THE FORM DIALOG formPromise = inputForm.show(formPrompt, buttonTitle) // VALIDATE FORM CONTENT inputForm.validate = function(formObject){ // EXTRACT VALUES FROM THE FORM’S VALUES OBJECT textValue = formObject.values['tagName'] // RETURN VALIDATION BOOLEAN return (!textValue) ? false:true } // PERFORM PROCESSES USING FORM DATA formPromise.then(function(formObject){ try { var targetTagName = formObject.values['tagName'] var targetTags = new Array() tags.apply(function(tag){ if(tag.name === targetTagName){ targetTags.push(tag) } }) if(targetTags.length > 0){ Timer.once(1,function(timer){ // wait for sheet/dialog document.windows[0].perspective = Perspective.BuiltIn.Tags Timer.once(1,function(timer){ // wait for perspective document.windows[0].selectObjects(targetTags) }) }) } else { var alert = new Alert("Tag Not Found", "The tag “" + targetTagName + "” was not found.\n\nDo you want to create it?") alert.addOption("Stop") alert.addOption("Create") alert.show(function(result){ if (result == 0){ console.log("Done") } else { Timer.once(1,function(timer){ // wait for sheet/dialog var tag = new Tag(targetTagName) document.windows[0].perspective = Perspective.BuiltIn.Tags Timer.once(1,function(timer){ // wait for perspective document.windows[0].selectObjects([tag]) }) }) } }) } } catch(err){ console.error(err) } }) // PROCESS FORM CANCELLATION formPromise.catch(function(err){ console.log("form cancelled", err.message) }) }); return action; })();

Deleting Tags

To delete one or more tags from the database, the deleteObject() function of the Database class is used, as in this script that deletes all tags from the database:

omnifocus://localhost/omnijs-run?script=try%7Btags%2EforEach%28%28tag%29%20%3D%3E%20%7BdeleteObject%28tag%29%7D%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
Deleting All Tags
 

tags.forEach(tag => {deleteObject(tag)})

And here’s a script that removes duplicate tags from the entire tag heirarchy:

omnifocus://localhost/omnijs-run?script=try%7Bvar%20tagNames%20%3D%20new%20Array%28%29%0Atags%2Eapply%28function%28tag%29%7B%0A%09if%20%28tagNames%2Eincludes%28tag%2Ename%29%29%7B%0A%09%09deleteObject%28tag%29%20%0A%09%7D%20else%20%7B%0A%09%09tagNames%2Epush%28tag%2Ename%29%0A%09%7D%0A%7D%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
Remove Duplicate Tags
 

var tagNames = new Array() tags.apply(function(tag){ if (tagNames.includes(tag.name)){ deleteObject(tag) } else { tagNames.push(tag.name) } })

NOTE: The previous script isn’t designed to examine any task relationships of the tags, it simply removes duplicate tags based upon the order they are encountered.

The following plug-in will delete all tags that have not been associated with items. Parent tags of tags who have been assigned will be retained.

Delete Unused Tags
 

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of-remove-unused-tags", "version": "1.5", "description": "This action will delete all tags that have not been associated with items.", "label": "Delete Unused Tags", "shortLabel": "Delete Tags" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // action code var deletionTotal = 0 do { var deletionCount = 0 tags.apply(tag => { // DO NOT REMOVE TAG GROUPS if (tag.children.length === 0){ console.log(tag.name) if (tag.tasks.length === 0){ console.log("Deleting tag: ", tag.name) deleteObject(tag) deletionCount = deletionCount + 1 } } }) deletionTotal = deletionTotal + deletionCount } while (deletionCount != 0); var msg = String(deletionTotal) + " tags were deleted." new Alert("Completed", msg).show() }); action.validate = function(selection, sender){ // validation code return (tags.length > 0) }; return action; })();

Clearing Tags from Tasks

To remove an assigned tag from its host task, use the removeTag(…) or removeTags(…) functions of the Task class.

Clear Tags from Selected Tasks
 

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of.clear-task-tags", "version": "1.1", "description": "This action will clear all tags assigned to the selected tasks.", "label": "Clear Tags from Tasks", "shortLabel": "Clear Task Tags" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ selection.tasks.forEach(function(task){ task.removeTags(task.tags) }) }); action.validate = function(selection, sender){ return (selection.tasks.length > 0) }; return action; })();

As an alternative, you can use the clearTags() function of both the Project and Task classes to remove applied tags:

For example, here is a plug-in that uses the clearTags() function to remove assigned tags from the selecte projects:

Clear Tags from Selected Projects
 

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of.clear-project-tags", "version": "1.2", "description": "This action will clear all tags assigned to the selected projects.", "label": "Clear Tags from Projects", "paletteLabel": "Clear Project Tags" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ selection.projects.forEach(project => { project.clearTags() }) }); action.validate = function(selection, sender){ return (selection.projects.length > 0) }; return action; })();

Importing Tags from Text File

An important aspect of the Omni Automation support in OmniFocus is in its ability to import data from sources outside of the application.

In the following example action plug-in, the user is prompted to choose a text file whose paragraphs comprise a list of tags to be added to the database. The chosen file is parsed, and each paragraph of the imported text is used to create a new top-level tag if an existing matching one does not exist.

Import Tags from File
 

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.import-text-to-tags", "version": "1.3", "description": "This action will convert the paragraphs of the chosen text file into OmniFocus tags.", "label": "Import Text to Tags", "paletteLabel": "Import Tags" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // get the names of existing tags var tagTitles = flattenedTags.map(tag => {return tag.name}) // show the file picker var picker = new FilePicker() picker.folders = false picker.multiple = false picker.types = [FileType.plainText] pickerPromise = picker.show() // process the results of the file picker pickerPromise.then(urlsArray => { aURL = urlsArray[0] aURL.fetch(data => { textArray = data.toString().split('\n') var newTagObjs = new Array() textArray.forEach(item =>{ if(item != "" && !tagTitles.includes(item)){ var tag = new Tag(item) tagTitles.push(item) newTagObjs.push(tag) } }) document.windows[0].perspective = Perspective.BuiltIn.Tags Timer.once(1,function(timer){ document.windows[0].selectObjects(newTagObjs) }) }) }) // if user cancels pickerPromise.catch(function(err){ console.log("picker cancelled", err.message) }) }); action.validate = function(selection, sender){ return true }; return action; })();

Copy Tags Between Selected Tasks

The following Omni Automaton plug-in uses the addTags(…) function of the Task class to copy the tags of a chosen task to the other tasks selected in the OmniFocus window.

To use, select both the tasks to receive the tags, and the task containing the set of tags to be copied, and run the action.

Copy Tags Between Selected Tasks
 

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of.copy-tags-between-tasks", "version": "2.0", "description": "This plug-in will copy the tags of the chosen task to the other selected tasks.", "label": "Copy Tags between Selected Tasks", "shortLabel": "Copy Tags" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // action code var tasks = selection.tasks // sort the task objects by name tasks.sort((a, b) => { var x = a.name.toLowerCase(); var y = b.name.toLowerCase(); if (x < y) {return -1;} if (x > y) {return 1;} return 0; }) // generate a list of task names var menuItems = tasks.map(task => {return task.name}) // generate a list of matching indexes var menuIndexes = new Array() menuItems.forEach((item,index) => {menuIndexes.push(index)}) var menuLabel = "Selected Tasks" var multiOptionMenu = new Form.Field.MultipleOptions( "menuKey", menuLabel, menuIndexes, menuItems, [] ) var checkboxSwitch = new Form.Field.Checkbox( "shouldReplaceTags", "Replace existing tags", false ) var inputForm = new Form() inputForm.addField(multiOptionMenu) inputForm.addField(checkboxSwitch) var formPrompt = "Select task to copy tags from:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ var indexes = formObject.values["menuKey"] // ensure at least one item is selected return (indexes.length === 1)?true:false } formPromise.then(function(formObject){ var indexes = formObject.values["menuKey"] var shouldReplaceTags = formObject.values["shouldReplaceTags"] var sourceTask = tasks[indexes[0]] var tagsToCopy = sourceTask.tags tasks.forEach(task => { if (shouldReplaceTags){task.clearTags()} task.addTags(tagsToCopy) }) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) }) }); action.validate = function(selection, sender){ // validation code return (selection.tasks.length > 1) }; return action; })();

Assigning Tags to Selected Tasks

This action will display a list of all tags (sorted), any or all of which may selected to be assigned to the selected tasks. The plug-in is designed to work only on iPadOS and iOS as their form interfaces scroll and allow for the display of dozens of tags.

Assign Tags to Selected Tasks (iPadOS/iOS)
 

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of.assign-tags-ipados-ios", "version": "1.5", "description": "(iPadOS/iOS) This action will display a list of all tags (sorted), any or all of which may selected to be assigned to the selected tasks.", "label": "Assign Tags", "shortLabel": "Assign Tags" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // action code // selection options: tasks, projects, folders, tags, allObjects var allTags = flattenedTags if (allTags.length > 1){ allTags.sort((a, b) => { var x = a.name.toLowerCase(); var y = b.name.toLowerCase(); if (x < y) {return -1;} if (x > y) {return 1;} return 0; }) } var menuItems = allTags.map(tag => {return tag.name}) var menuIndexes = new Array() menuItems.forEach((item,index) => {menuIndexes.push(index)}) var menuLabel = "Assign Tags" var multiOptionMenu = new Form.Field.MultipleOptions( "menuKey", menuLabel, menuIndexes, menuItems, [] ) var inputForm = new Form() inputForm.addField(multiOptionMenu) var formPrompt = "Select one or more tags to apply to selected tasks:" var buttonTitle = "Continue" var formPromise = inputForm.show(formPrompt,buttonTitle) inputForm.validate = function(formObject){ var indexes = formObject.values["menuKey"] return (indexes.length > 0)?true:false } formPromise.then(function(formObject){ var indexes = formObject.values["menuKey"] var tags = new Array() indexes.forEach(index => { tags.push(allTags[index]) }) selection.tasks.forEach(function(task){ task.addTags(tags) }) }) formPromise.catch(function(err){ console.error("form cancelled", err.message) }) }); action.validate = function(selection, sender){ // validation code // selection options: tasks, projects, folders, tags, allObjects return ( Device.current.iOS && selection.tasks.length > 0 && tags.length > 0 ) }; return action; })();

Toggle Status of Selected Tag

This plug-in will toggle the status of the selected tag between active and on hold. If the selected tag is a tag group, the status of all of the group tags will changed accordingly.

NOTE: this plug-in uses the sender parameter of the action handlers to toggle the text of the plug-in menu item.

Toggle Status of Selected Tag
 

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of.toggle-tag-status", "version": "1.0", "description": "This plug-in will toggle the status of the selected tag between active and on hold. If the selected tag is a tag group, the status of all of the group tags will changed accordingly.", "label": "Toggle Tag Status", "shortLabel": "Toggle Tag" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // action code // selection options: tasks, projects, folders, tags, allObjects var tag = selection.tags[0] if(tag.status === Tag.Status.Active){ tag.status = Tag.Status.OnHold tag.flattenedTags.forEach(tg => { tg.status = Tag.Status.OnHold }) } else { tag.status = Tag.Status.Active tag.flattenedTags.forEach(tg => { tg.status = Tag.Status.Active }) } }); action.validate = function(selection, sender){ // validation code // selection options: tasks, projects, folders, tags, allObjects // sender can be: MenuItem, ToolbarItem, or undefined (remote script) if (selection.tags.length === 1){ if (sender instanceof MenuItem){ var menuText = (selection.tags[0].status === Tag.Status.Active)?'Set Status to On Hold':'Set Status to Active' sender.label = menuText } return true } if (sender instanceof MenuItem){sender.label = 'Toggle Tag Status'} return false }; return action; })();