“Actions” or “Tasks”
When interacting with the OmniFocus application’s user-interface (UI for short), you will encounter the term “Action” which refers to a new to-do item for a project. However, in the OmniPlan application, such a time-related element is called a “Task.”
The Omni Automation support in OmniFocus considers the terms “action” and “task” to be functionally synonymous, and refers to the scriptable element representing an “action” as a “task.”
In other words: a “task” is a something that needs doing, and “action” is the element in the OmniFocus interface that represents that need or to-do item. Scripts reference tasks, and the graphical user-interface of OmniFocus references actions. In the following documentation, consider tasks to be equivalent to actions.
Properties
An instance of the Task class is defined by the value of its properties.
added (inherited from DatedObject class) (Date or null r/o) • Returns the date the object was first saved. If the object is newly inserted, this will be null. For newly inserted objects, the added property may be set (but once an object is saved for the first time, the property is read-only).
after (Task.ChildInsertionLocation r/o) • A positional indicator that reference the list posotion directly following this task instance.
assignedContainer (Project, Task, or Inbox or null) • For tasks in the inbox, the tentatively assigned project or parent task, which will be applied on cleanup.
attachments (Array of FileWrapper) • An array of FileWrapper objects representing the attachments associated with the task. See related documentation.
before (Task.ChildInsertionLocation r/o) • A positional indicator that references the list posotion immediately preceding this task instance.
beginning (Task.ChildInsertionLocation r/o) • A positional indicator that references the very start of the task’s container object.
beginningOfTags (Task.TagInsertionLocation r/o) • Returns a location indicating the position before all of the Tasks tags.
children (Array of Task r/o) • Returns all the child tasks of this task, sorted by library order.
completed (Boolean r/o) • True if the task has been marked completed. Note that a task may be effectively considered completed if a containing task is marked completed.
completedByChildren (Boolean) • If set, the Task will be automatically marked completed when its last child Task is marked completed.
completionDate (Date or null r/o) • If set, the Task is completed.
containingProject (Project or null r/o) • The Project that this Task is contained in, either as the root of the project or indirectly from a parent task. If this task is in the inbox, then this will be null.
deferDate (Date or null) • If set, the Task is not actionable until this date.
dropDate (Date or null r/o) • If set, the Task is dropped.
dueDate (Date or null) • If set, the Task should be completed by this date.
effectiveCompletedDate (Date or null r/o) • (v3.8) Returns the computed effective completion date for the Task, based on its local completionDate and those of its containers.
effectiveDeferDate (Date or null r/o) • Returns the computed effective defer date for the Task, based on its local deferDate and those of its containers.
effectiveDropDate (Date or null r/o) • (v3.8) Returns the computed effective drop date for the Task, based on its local dropDate and those of its containers.
effectiveDueDate (Date or null r/o) • Returns the computed effective due date for the Task, based on its local dateDue and those of its containers.
effectiveFlagged (Boolean r/o) • Returns the computed effective flagged status for the Task, based on its local flagged and those of its containers.
estimatedMinutes (Number or null) • (macOS v3.5) The estimated number of minutes this task will take to finish, or null if no estimate has been made.
ending (Task.ChildInsertionLocation r/o) • A positional indicator that references the position at the very end of the task’s container object.
endingOfTags (Task.TagInsertionLocation r/o) • Returns a location indicating the position after all of the Tasks tags.
flagged (Boolean) • The flagged status of the task.
flattenedChildren (TaskArray r/o) • An alias for flattenedTasks.
flattenedTasks (TaskArray r/o) • Returns a flat array of all tasks contained within this task. Tasks are sorted by their order in the database. This flat array is often used for processing the entire task hierarchy of a specific task.
hasChildren (Boolean r/o) • Returns true if this task has children, more efficiently than checking if children is empty.
inInbox (Boolean r/o) • Returns true if the task is a direct child of the inbox, but not if the task is contained by another task that is in the inbox.
linkedFileURLs (Array of URL r/o) • The list of file URLs linked to this task. The files at these URLs are not present in the database, rather the database holds bookmarks leading to these files. These links can be read on iOS, but not written to.
modified (inherited from DatedObject class) (Date or null r/o) • Returns the date the object was most recently modified. If the object is newly inserted, this will be null. For newly inserted objects, the modified property may be set (but once an object is saved for the first time, the property is read-only).
name (String) • The title of the task.
note (String) • The note of the task.
notifications (Array of Task.Notification r/o) • An array of the notifications that are active for this task. (see related documentation)
parent (Task or null r/o) • The parent Task which contains this task.
project (Project or null r/o) • The Project that this Task is the root task of, or null if this task is in the inbox or contained by another task.
repetitionRule (Task.RepetitionRule or null r/o) • The object holding the repetition properties for this task, or null if it is not repeating. See related documentation.
sequential (Boolean) • If true, then children of this task form a dependency chain. For example, the first task blocks the second one until the first is completed.
shouldUseFloatingTimeZone (Boolean) • (v3.6) When set, the dueDate and deferDate properties will use floating time zones. (Note: if a Task has no due or defer dates assigned, this property will revert to the database’s default setting.)
tags (TagArray r/o) • Returns the Tags associated with this Task.
taskStatus (Task.Status r/o) • Returns the current status of the task.
tasks (TaskArray r/o) • Returns all the tasks contained directly in this task, sorted by their library order.
IMPORTANT: some of these properties have values that are JavaScript date objects. An overview of some of the techniques for generating data objects in Omni Automation are detailed in the shared Date class documentation.
Task Properties Example
The following function uses multiple properties of the Task class to return “path string” detailing the parent hierarchy of the provided task reference:
"Smith Project ▸ Setup Task ▸ Preparation Task ▸ Gather Data Task"
Derive “Path” of Task
function getTaskPath(task){
getPath = function(task){
if (task.inInbox){ // inbox task
return task.name
} else if (task.project){ // root of project
return task.containingProject.name
} else {
return getPath(task.parent) + " ▸ " + task.name
}
}
return getPath(task)
}
Task.ChildInsertionLocation Class
A location specified relative to an existing Task or Database. These cannot be instantiated directly, rather they are returned from properties like Task.before, Inbox.ending, or Project.beginning. (For a complete list of locations, open the API window navigation sidebar and use its filter to search for Task.ChildInsertionLocation.)
LOCATIONS: Inbox.beginning, Inbox.ending, Task.after, Task.before, Task.beginning, Task.ending, Project.beginning, Project.ending
Task.TagInsertionLocation Class
A location specifying the order of a Tag within a Task. These cannot be instantiated directly, rather they are returned from properties like <TagInstance>.beforeTag() or <TagInstance>.endingOfTags. (For a complete list of locations, open the navigation sidebar and use its filter to search for Task.TagInsertionLocation.)
LOCATIONS: <TagInstance>.beginningOfTags, <TagInstance>.endingOfTags
Ordering Tags
task = new Task("Tag Ordering Example")
tagRed = flattenedTags.byName("Red") || new Tag("Red")
tagGreen = flattenedTags.byName("Green") || new Tag("Green")
tagBlue = flattenedTags.byName("Blue") || new Tag("Blue")
task.addTag(tagBlue)
insertionLocation = task.beforeTag(tagBlue)
task.addTag(tagGreen, insertionLocation)
task.addTag(tagRed, task.beginningOfTags)
id = task.id.primaryKey
URL.fromString(`omnifocus:///task/${id}`).open()
Examples
The following script shows how to drop a task:
Drop Selected Tasks
win = document.windows[0]
tasks = win.selection.tasks
tasks.forEach(task => {
task.active = false
})
The following script uses the dropDate property of the Task class and the flattenedTasks property of the Database class to revive tasks that were dropped since the start of the day (12:00 AM):
Un-Drop Tasks Dropped Today
cal = Calendar.current
now = new Date()
midnightToday = cal.startOfDay(now)
dc = cal.dateComponentsFromDate(midnightToday)
dc.day = dc.day + 1
midnightTomorrow = cal.dateFromDateComponents(dc)
flattenedTasks.forEach(task => {
tdDate = task.dropDate
if (tdDate >= midnightToday && tdDate < midnightTomorrow){
task.active = true
}
})
Un-Drop Tasks Dropped Yesterday
cal = Calendar.current
now = new Date()
midnightToday = cal.startOfDay(now)
dc = cal.dateComponentsFromDate(midnightToday)
dc.day = dc.day - 1
midnightYesterday = cal.dateFromDateComponents(dc)
flattenedTasks.forEach(task => {
tdDate = task.dropDate
if (tdDate >= midnightYesterday && tdDate < midnightToday){
task.active = true
}
})
NOTE: Both of the previous script examples use properties and functions of the Calendar class to perform date calculations.
Set Floating Timezone Property
The following plug-in will set the value of the shouldUseFloatingTimeZone property for all existing tasks:
Set Time Zone Type for All Tasks
/*{
"type": "action",
"targets": ["omnifocus"],
"author": "Otto Automator",
"identifier": "com.omni-automation.setTimeZoneTypeForAllTasks",
"version": "1.2",
"description": "Converts the time zone type for all tasks to the chosen type.",
"label": "Set Time Zone Type for All Tasks",
"shortLabel": "Set Time Zone Types",
"paletteLabel": "Set Time Zone Types",
"image": "timelapse"
}*/
(() => {
const action = new PlugIn.Action(async function(selection, sender){
timezoneTypesMenu = new Form.Field.Option(
"timezoneType",
null,
[0,1],
["Current Time Zone", "Floating Time Zone"],
0
)
inputForm = new Form()
inputForm.addField(timezoneTypesMenu)
inputForm.validate = function(formObject){
return true
}
formPrompt = "Choose the time zone type for all tasks:"
buttonTitle = "Continue"
formObject = await inputForm.show(formPrompt,buttonTitle)
typeIndex = formObject.values['timezoneType']
flattenedTasks.forEach((task) => {
task.shouldUseFloatingTimeZone = [false, true][typeIndex]
})
});
action.validate = function(selection, sender){
return true
};
return action;
})();
The TaskArray Class
An Array containing Task objects. Some of the properties of the Task class have values that are instances of the TaskArray class.
The TaskArray class offers a function for locating a task within the array by the name of the task.
byName(name:String) → (Task or null) • Returns the first Task contained directly in this array with the given name.
Here is an example script that uses the flattenedTasks of the Database class to reveal the first occurence of a task by name in the database:
Reveal First Named Task
task = flattenedTasks.byName("Clean Gutters")
if(task){
id = task.id.primaryKey
URL.fromString("omnifocus:///task/" + id).open()
}
To locate all tasks named with a specified name, use the JavaScript filter() function called on an instance of the TaskArray class, such as the flattenedTasks property of the Database class:
Locate Tasks with Specified Name
taskName = "Summary"
matchedTasks = flattenedTasks.filter(task => {
return task.name === taskName
})
RELATED: In addition to the name property, database objects are sometimes identified by their unique identifier string. The Task class offers a global function for locating a task by identifier:
Task.byIdentifier(identifier: String) → (Task or null) • Returns the Task with the specified identifier, or null if no such task exists.
ID of Selected Task
win = document.windows[0]
tasks = win.selection.tasks
if (tasks.length === 1){
taskID = tasks[0].id.primaryKey
console.log(taskID)
new Alert("Task ID",taskID).show()
}
Reveal Task by ID
taskID = "dXL1Kdp4XCx"
task = Task.byIdentifier(taskID)
if(task){
urlStr = "omnifocus:///task/" + taskID
URL.fromString(urlStr).open()
}
The Task.Status Class
The values for the taskStatus property of the Task class:
Available (Task.Status r/o) • The task is available to work on.
Blocked (Task.Status r/o) • The task is not available to work on currently, due to a future defer date, a preceeding task in a sequential project, or having an on-hold tag associated.
Completed (Task.Status r/o) • The task is already completed.
Dropped (Task.Status r/o) • The task will not be worked on.
DueSoon (Task.Status r/o) • The task is incomplete and due soon.
Next (Task.Status r/o) • The task is the first available task in a project.
Overdue (Task.Status r/o) • The task is incomplete overdue.
all (Array of Task.Status r/o) • An array of all items of this enumeration.
Flag Tasks that are Due Soon
tasks = flattenedTasks.filter(task => {
return task.taskStatus === Task.Status.DueSoon
})
if (tasks.length > 0){
tasks.forEach(task => {task.flagged = true})
document.windows[0].perspective = Perspective.BuiltIn.Flagged
}
Creating Tasks
To create an instance of the Task class, the standard JavaScript new item constructor is used:
new Task(name:String, position:Project, Task, or Task.ChildInsertionLocation or null) → (Task) • Returns a new Task at the given location. If a project or task is given as a location, the new task is placed at the end of the children of that parent. If no location is specified, then the task is created at the end of the inbox.
New Task at End of Inbox
task = new Task("My Task")
New Task at Beginning of Inbox
task = new Task("My Task", inbox.beginning)
Once a task instance has been created, the value of its properties can be set:
New Task with Properties
task = new Task("My Task", inbox.beginning)
task.note = "This is the note for the created task."
task.flagged = true
Here's an example script that creates and then displays a task in the application interface:
Make and Show New Task
task = new Task("My New Task", inbox.beginning)
urlString = "omnifocus:///task/" + task.id.primaryKey
URL.fromString(urlString).open()
(1) The JavaScript new item constructor takes the name for the new task as input, and optionally the insertion postion in the task’s parent container, as a second input parameter. The result is an object reference to the newly created task, which is stored in the variable: task
(2) Every element of the OmniFocus database, including tasks, projects, and folders, shares the id and primaryKey properties, which generate a unique identifier string for each element. This identifer can be appended to an OmniFocus URL that will cause the identified item to display in the app UI when the URL is called.
(3) The URL string is converted into a URL object using the fromString(…) method of the URL class, which is then called using the open( ) method of the same class.
Transport Text Parsing
Omni Automation support in OmniFocus offers a special class function for converting text instructions into new tasks. The syntax for the task creation instructions uses common characters and constructs that you can use as shorthand way of indicating tasks. When the text containing the instructions is parsed using the byParsingTransportText(…) function, new tasks are created.
byParsingTransportText(text: String, singleTask: Boolean or null) → (Array of Task) • Returns an array of tasks by parsing the transport text formatted input. Optionally, only the first task can be requested (but will still be returned in an array).
In the following example, a new task is created in the specified existing project. The due date/time are assigned along with a specified existing tag.
Create Task(s) by Parsing Text
txt = `New Task!::Existing Project Title #12/12/20 8.30a $3h @existing-tag-name//This is the task note`
newTasks = Task.byParsingTransportText(txt, true)
taskID = newTasks[0].id.primaryKey
URL.fromString("omnifocus:///task/" + taskID).open()
Here are the syntax rules for the task-creation shorthand:
The first line and any other lines starting with -- (double-hyphens) become new actions (tasks). Other lines become notes for the preceding action.
To specify a host project, use > (greater-than sign) or :: (double-colons), followed by a project name or abbreviation. The colons are nicer for the iPhone because they are on the first shifted keyboard rather than the less-accessible math keyboard. The project string is matched exactly as if it was entered in a project cell in OmniFocus.
To specify a tag, use @ (at sign), followed by a tag name or abbreviation. Like project names, the context name is matched exactly as it would be in OmniFocus.
To enter start or due dates, use # (number sign), followed by some date expression (like 9-30-2014). If there is only one date, it becomes the due date. If there are two (each with its own number sign), the first becomes the start date and the second becomes the due date.
To enter a time estimate, use $ (dollar sign—time is money) followed by some duration expression (like 5m, 1h, and so on); you can use the same duration expressions that you use in OmniFocus.
To flag the action, use ! (exclamation point) at the end of the action title.
You can also add a note on the same line as an action title by separating them with // (double-slashes). Anything after the double-slashes becomes a note, but double-slashes in a URL like omnigroup.com don’t count.
As a “use-case” example, here’s a Drafts app action for parsing the content of the current Drafts document as OmniFocus Transport Text. Copy and paste this script into the Drafts app Action Editor.
Drafts app Action
(() => {
// wrapping the action script in a self-invoking anonymous arrow function (() => {})();
// prevents possible conflicts between script actions that may use the same variable names
// OmniFocus JavaScript Context
// Omni Automation script as a function with text input
function executeTransportTextString(transportString){
newTasks = Task.byParsingTransportText(transportString, null)
taskID = newTasks[0].id.primaryKey
URL.fromString("omnifocus:///task/" + taskID).open()
};
// Host Application JavaScript Context (1Writer, Drafts, etc.)
const transportString = editor.getText();
// create and execute an Omni Automation script URL
const functionString = executeTransportTextString.toString();
const encodedFunction = encodeURIComponent(functionString);
const inputString = JSON.stringify(transportString);
const encodedInput = encodeURIComponent(inputString);
const op = "%28";
const cp = "%29";
const scriptURL = `omnifocus://localhost/omnijs-run?script=${op}${encodedFunction}${cp}${op}argument${cp}&arg=${encodedInput}`;
app.openURL(scriptURL);
})();
The initial execution of the Drafts action will cause the display of the Omni Automation script security dialog. Select the checkbox option to run the script without re-approval and subsequent executions will be without a security dialog.
More information regarding the creation of Omni Automation script URLs can be found here.
Related Omni Automation Plug-In
DOWNLOAD a plug-in compatible with all Omni apps that parses the clipboard as Transport Text to generate new tasks in OmniFocus.
Task Instance Functions
Here are the functions that can be performed with an instance of the Task class:
taskNamed(name:String) → (Task or null) • Returns the first child Task in this task with the given name, or null.
childNamed(name:String) → (Task or null) • An alias for taskNamed function.
appendStringToNote(stringToAppend: String) → ( ) • Appends stringToAppend to the end of the Task’s note.
drop(allOccurrences:Boolean) → ( ) • (v3.8) Drops this Task. If true is passed in for allOccurrences then this task will not repeat, even if it has a repititionRule set on it. If false is passed in for allOccurrences, this task will repeat as normal.
addNotification(info: Number or Date) → (Task.Notification) • (v3.8) Add a notification from the specification in info. Supplying a Date creates an absolute notification that will fire at that date. Supplying a Double (number) will create a due-relative notification. Specifying a due relative notification when this task’s effectiveDueDate is not set will result in an error.
removeNotification(notification:Task.Notification) → ( ) • (v3.8) Remove an active notification for this task. Supplying a notification that is not in this task’s notifications array, or a notification that has task to something other than this task results in an error.
markComplete(date:Date or null) → ( ) • If the task is not completed, marks it as complete with the given completion date (or the current date if no date is specified). For repeating tasks, this makes a clone of the task and marks that clone as completed. In either case, the task that has been marked completed is returned.
markIncomplete() → ( ) • If the task is completed, marks it as incomplete.
addLinkedFileURL(url: URL) → ( ) • Links a file URL to this task. In order to be considered a file URL, url must have the file scheme. That is, url must be of the form file://path-to-file. The file at url will not be added to database, rather a bookmark leading to it will be added. In order to add files to a task, use the addAttachment function. Linking files is especially useful for large files, as including large files in the database can degrade app performance.
removeLinkedFileWithURL(url: URL) → ( ) • Removes the first link to a file with the given url. This removes the bookmark that leads to the file at url. If the file itself is present in the database, use the removeAttachmentAtIndex function instead.
addAttachment(attachment: FileWrapper) → ( ) • Adds attachment as an attachment to the task. If the attachment is large, consider using the addLinkedFileURL function instead. Including large attachments in the database can degrade app performance. (see related documentation)
removeAttachmentAtIndex(index: Number) → ( ) • Removes the attachment at index from this task’s attachments array. (see related documentation)
apply(function:Function) → (ApplyResult or null) • Calls the given function for this Task and recursively into any child task.
Tag-Editing Functions
addTag(tag:Tag) → ( ) • Adds a Tag to this task, appending it to the end of the list of associated tags. If the tag is already present, no change is made. The Database function moveTags() can be used to control the ordering of tags within the task.
addTags(tags:Array of Tag) → ( ) • Adds multiple Tags to this task, appending them to the end of the list of associated tags. For any tags already associated with the Task, no change is made. The Database function moveTags() can be used to control the ordering of tags within the task.
removeTag(tag:Tag) → ( ) • Removes a Tag from this task. If the tag is not associated with this task, no change is made.
removeTags(tags:Array of Tag) → ( ) • Removes multiple Tags from this task. If a tag is not associated with this task, no change is made.
clearTags() → ( ) • (v3.8) Clears all Tags assigned to this task.
Tag-Ordering Functions
afterTag(tag: Tag or null) → (Task.TagInsertionLocation) • (v4.0) Returns a location indicating the position after an existing tag in the Task’s tags. If no peer Tag is specified, or the the specified tag is not in the task’s tags, this is equivalent to endingOfTags.
beforeTag(tag: Tag or null) → (Task.TagInsertionLocation) • (v4.0) Returns a location indicating the position before an existing tag in the Task’s tags. If no peer Tag is specified, or the the specified tag is not in the task’s tags, this is equivalent to beginningOfTags.
moveTag(tag: Tag, location: Task.TagInsertionLocation) • (v4.0) Moves an existing associated Tag within the task’s list of tags. If the tag is not associated with the task, no change is made.
moveTags(tags: Array of Tag, location: Task.TagInsertionLocation) • (v4.0) Moves a list of associated Tags within the task’s list of tags. Any tags not currently associated with the task are ignored.
Working with Tasks
The following script examples demonstrate how to use some of the functions of the Task class:
This first script demonstrates how to retrieve references to the currently selected tasks. The result will be an array of object references, or an empty array if no tasks are selected.
Get Selected Tasks
document.windows[0].selection.tasks
The value of the flattenedTasks property of the Database class is used to retrieve object references to all tasks in the database:
Retrieve References to All TasksGetting object references to all tasks in the Inbox:
flattenedTasks
Get All Tasks in Inbox
masterTaskArray = new Array()
inbox.apply(task => masterTaskArray.push(task))
console.log(masterTaskArray)
The next script uses the apply(…) function to generate an array of object references to all of the tasks contained within the entire hierarchy of folders and projects:
Get All Tasks Contained in Projects in the Library
masterTaskArray = new Array()
library.apply(function(item){
if (item instanceof Project && item.task.hasChildren){
masterTaskArray.push(item.task.children)
}
})
console.log(masterTaskArray)
The following script will focus any projects that contain tasks that are due soon:
Focus Projects with Tasks Due Soon
parentProjects = new Array()
flattenedProjects.forEach(project => {
if (project.hasChildren){
project.children.forEach(task => {
if (task.taskStatus === Task.Status.DueSoon){
if (!parentProjects.includes(project)){
parentProjects.push(project)
}
}
})
}
})
if (parentProjects.length > 0){
document.windows[0].perspective = Perspective.BuiltIn.Projects
document.windows[0].focus = parentProjects
}
Using the added property of the Database Object class to sort all tasks by the date they were added to the database:
Sort Tasks by Creation Date
tasks = flattenedTasks
tasks.sort((a, b) => {
var x = a.added;
var y = b.added;
if (x < y) {return -1;}
if (x > y) {return 1;}
return 0;
})
tasks.map(task => {return task.name})
Marking the selected tasks complete:
Mark Selected Tasks Complete
(async () => {
try {
items = document.windows[0].selection.tasks
if(items.length === 0){
throw {
name: "Selection Issue",
message: "Please select one or more tasks."
}
}
items.forEach(item => {item.markComplete()})
}
catch(err){
if(!err.message.includes("cancelled")){
await new Alert(err.name, err.message).show()
}
throw `${err.name}\n${err.message}`
}
})();
Assigning Tags to Tasks
The addTag(…) and addTags(…) functions of the Task class are used to assign tags to tasks. These functions require object references to the tags to add as the function input. The following script uses the flattenedTags property of the Database class to generate a reference to an existing tag, creating a new tag if needed:
Get Tag Reference by Name
tag = flattenedTags.byName("Conifer") || new Tag("Conifer")
The previous technique for generating a tag reference is used to apply a specified tag to the selected tasks:
Apply Tag to Selected Tasks
tagName = "Redwood"
tag = flattenedTags.byName(tagName) || new Tag(tagName)
tasks = document.windows[0].selection.tasks
tasks.forEach(task => {task.addTag(tag)})
Using similar techniques and the addTags(…) function to apply an array of tags to each of the selected tasks:
Apply Tags to Selected Tasks
tagTitles = ["Northeast","Atlantic Seaboard","New England","Rhode Island"]
tagRefs = new Array()
tagTitles.forEach(title => {
tagObj = flattenedTags.byName(title) || new Tag(title)
tagRefs.push(tagObj)
})
tasks = document.windows[0].selection.tasks
tasks.forEach(task => {task.addTags(tagRefs)})
Plug-In: Open Link in Note
This Omni Automaton plug-in will scan the note text of the selected project or task, and if it locates an URL in the text, will open the found URL.
Open Project|Action Note URL
/*{
"type": "action",
"targets": ["omnifocus"],
"author": "Otto Automator",
"identifier": "com.omni-automation.call-note-url",
"version": "1.6",
"description": "This action will open the URL string that is the value of the note of the selected action.",
"label": "Open Note URL",
"shortLabel": "Open Note URL",
"paletteLabel": "Open Note URL",
"image": "link.circle"
}*/
(() => {
const action = new PlugIn.Action(function(selection, sender){
note = (selection.projects.length === 1 ? selection.projects[0].task.note : selection.tasks[0].note)
if (note && note.includes("://")) {
urlStr = note.match(/[^<\s][^\s]+\/\/[^\s>]+/)[0]
url = URL.fromString(urlStr)
if(url){
url.open()
} else {
console.error("ERROR: \"" + urlStr + "\" is not a valid URL.")
}
}
});
action.validate = function(selection, sender){
return (
selection.projects.length === 1 ||
selection.tasks.length === 1
)
};
return action;
})();
Move Tasks
Here are two Omni Automation Plug-Ins for moving selected tasks into either a new task group or a new project.
Move Selected Tasks into New Project
/*{
"type": "action",
"targets": ["omnifocus"],
"author": "Otto Automator",
"identifier": "com.omni-automation.of.move-selected-tasks-into-project",
"version": "1.3",
"description": "Move the selected tasks into a new top-level project.",
"label": "Move Selected Tasks into New Project",
"shortLabel": "Move Tasks",
"paletteLabel": "Move Tasks",
"image": "square.and.arrow.down.fill"
}*/
(() => {
const action = new PlugIn.Action(async function(selection, sender){
// CONSTRUCT THE FORM
inputForm = new Form()
// CREATE FORM ELEMENT: TEXT INPUT
textField = new Form.Field.String("projectName", "Project Name", null)
// CREATE FORM ELEMENT: OPTION MENU
popupMenu = new Form.Field.Option(
"projectType",
"Project Type",
[0, 1, 2],
["Parallel","Sequential","Single Actions"],
0
)
// ADD THE ELEMENTS TO THE FORM
inputForm.addField(textField)
inputForm.addField(popupMenu)
// VALIDATE FORM CONTENT
inputForm.validate = function(formObject){
// EXTRACT VALUES FROM THE FORM’S VALUES OBJECT
textValue = formObject.values['projectName']
return ((textValue) ? true:false)
}
// DIALOG PROMPT AND OK BUTTON TITLE
formPrompt = "Enter the name for the new top-level project and select its project type:"
buttonTitle = "Continue"
// DISPLAY THE FORM DIALOG
formObject = await inputForm.show(formPrompt, buttonTitle)
// PERFORM PROCESSES USING FORM DATA
textValue = formObject.values['projectName']
menuItemIndex = formObject.values['projectType']
// CREATE PROJECT AND MOVE TASKS
project = new Project(textValue)
moveTasks(selection.tasks, project)
// SET THE PROJECT TYPE
if (menuItemIndex === 1){
project.task.sequential = true
} else if (menuItemIndex === 2){
project.containsSingletonActions = true
}
// SHOW THE PROJECT
projID = project.id.primaryKey
urlStr = "omnifocus:///task/" + projID
URL.fromString(urlStr).open()
});
action.validate = function(selection, sender){
return (selection.tasks.length > 0)
};
return action;
})();
Move Selected Tasks into New Action Group
/*{
"type": "action",
"targets": ["omnifocus"],
"author": "Otto Automator",
"identifier": "com.omni-automation.of.move-selected-tasks-into-new-action",
"version": "1.4",
"description": "Move the selected tasks into a new top-level action group.",
"label": "Move Selected Tasks into New Action Group",
"shortLabel": "Move Tasks",
"paletteLabel": "Move Tasks",
"image": "archivebox.circle"
}*/
(() => {
const action = new PlugIn.Action(async function(selection, sender){
// CONSTRUCT THE FORM
inputForm = new Form()
// CREATE FORM ELEMENTS: TEXT INPUT
textField = new Form.Field.String("groupName", null, null)
// CREATE FORM ELEMENT: OPTION MENU
popupMenu = new Form.Field.Option(
"actionType",
"Action Type",
[0, 1],
["Parallel","Sequential"],
0
)
// ADD THE ELEMENTS TO THE FORM
inputForm.addField(textField)
inputForm.addField(popupMenu)
// VALIDATE FORM CONTENT
inputForm.validate = function(formObject){
// EXTRACT VALUES FROM THE FORM’S VALUES OBJECT
textValue = formObject.values['groupName']
return ((textValue) ? true:false)
}
// DIALOG PROMPT AND OK BUTTON TITLE
formPrompt = "Provide name and type for new top-level action group:"
buttonTitle = "Continue"
// DISPLAY THE FORM DIALOG
formObject = await inputForm.show(formPrompt, buttonTitle)
// PERFORM PROCESSES USING FORM DATA
textValue = formObject.values['groupName']
menuItemIndex = formObject.values['actionType']
taskGroup = new Task(textValue)
moveTasks(selection.tasks, taskGroup)
// SET THE PROJECT TYPE
if (menuItemIndex === 1){
taskGroup.sequential = true
}
// SHOW THE ACTION
taskID = taskGroup.id.primaryKey
urlStr = "omnifocus:///task/" + taskID
URL.fromString(urlStr).open()
});
action.validate = function(selection, sender){
return (selection.tasks.length > 0)
};
return action;
})();
Display Host Project
If you have a project task selected, this plug-in will feature the containing project in the OmniFocus window:
Display Host Project
/*{
"type": "action",
"targets": ["omnifocus"],
"author": "Otto Automator",
"identifier": "com.omni-automation.of.display-host-project",
"version": "1.7",
"description": "This action will display the host project of the selected task.",
"label": "Display Host Project",
"shortLabel": "Display Project",
"paletteLabel": "Display Project",
"image": "archivebox"
}*/
(() => {
const action = new PlugIn.Action(async function(selection, sender){
project = selection.tasks[0].containingProject
projID = project.id.primaryKey
urlStr = "omnifocus:///task/" + projID
URL.fromString(urlStr).open()
});
action.validate = function(selection, sender){
return (
selection.tasks.length === 1 &&
selection.tasks[0].containingProject
)
};
return action;
})();
Delete All Tasks Tagged with Specific Tag
Here’s an Omni Automation action that will delete all of the tasks that are tagged with the tag specified by the user.
Delete All Tasks Tagged with Tag
/*{
"type": "action",
"targets": ["omnifocus"],
"author": "Otto Automator",
"identifier": "com.omni-automation.com.of.delete-tasks-with-tag",
"version": "1.1",
"description": "This action will delete all tasks that have been tagged with the specified tag.",
"label": "Delete All Tasks with Tag",
"shortLabel": "Delete Tasks with Tag",
"paletteLabel": "Delete Tasks with Tag",
"image": "tag.slash"
}*/
(() => {
const action = new PlugIn.Action(async function(selection, sender){
try {
tagNames = new Array()
tags.apply(tag => tagNames.push(tag.name))
if (tagNames.length === 0){throw Error("No tags in database")}
textInputField = new Form.Field.String(
"textInput",
null,
null
)
inputForm = new Form()
inputForm.addField(textInputField)
inputForm.validate = function(formObject){
var inputText = formObject.values['textInput']
var textStatus = ((!inputText)?false:true)
return (textStatus && tagNames.includes(inputText)) ? true : false
}
formPrompt = "Enter the title of the tag whose tasks will be deleted:"
buttonTitle = "Continue"
formObject = await inputForm.show(formPrompt,buttonTitle)
tagTitle = formObject.values['textInput']
targetTag = null
tags.apply(function(tag){
if(tag.name == tagTitle){
targetTag = tag
return ApplyResult.Stop
}
})
targetTasks = targetTag.tasks
targetTasks.forEach(task => {
deleteObject(task)
})
msg = String(targetTasks.length) + " task(s) have been deleted."
new Alert("STATUS", msg).show()
}
catch (err){
new Alert('SCRIPT ERROR', err.message).show()
}
});
action.validate = function(selection, sender){
return true
};
return action;
})();
Save Task as Calendar File
This action will save the data of the selected task to a standard iCalendar file.
iCal File for Task
/*{
"type": "action",
"targets": ["omnifocus"],
"author": "Otto Automator",
"identifier": "com.omni-automation.of.ics-file-for-task",
"version": "1.4",
"description": "This action will create an iCal (ics) file in the OmniFocus documents folder, matching the parameters of the currently selected task. On macOS, the file will be automatically opened causing the Calendar application to present a new event dialog.",
"label": "iCal File for Task",
"shortLabel": "iCal File",
"image": "calendar.badge.plus"
}*/
(() => {
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function dateToCalDateStamp(date){
var dateStamp = date.toISOString().split("-").join("").split(":").join("").replace(".","")
dateStamp = dateStamp.substring(0, dateStamp.length - 4) + "Z"
return dateStamp
}
const action = new PlugIn.Action(async function(selection, sender){
try {
task = selection.tasks[0]
taskID = task.id.primaryKey
taskURL = "omnifocus:///task/" + taskID
taskTitle = task.name
taskNote = task.note
if (!taskNote){
taskNote = ""
} else {
// Replace CR+LF and LF newlines with \n for iCal format
taskNote = taskNote.replace(/\n/g, '\\n');
}
taskDueDateStamp = dateToCalDateStamp(task.dueDate)
taskDuration = task.estimatedMinutes
if (!taskDuration){taskDuration = 60}
taskEndDate = task.dueDate
taskEndDate = new Date(
taskEndDate.setMinutes(taskEndDate.getMinutes() + taskDuration)
)
taskEndDateStamp = dateToCalDateStamp(taskEndDate)
dateStamp = dateToCalDateStamp(new Date())
uid = uuidv4()
calEventString = `BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//omni-automation.com
CALSCALE:GREGORIAN
METHOD:PUBLISH
BEGIN:VEVENT
SUMMARY:${taskTitle}
UID:${uid}
SEQUENCE:0
STATUS:CONFIRMED
TRANSP:TRANSPARENT
RRULE:
DTSTART:${taskDueDateStamp}
DTEND:${taskEndDateStamp}
DTSTAMP:${dateStamp}
CATEGORIES:
LOCATION:
GEO:
DESCRIPTION:${taskNote}
URL:${taskURL}
END:VEVENT
END:VCALENDAR`
console.log(calEventString)
filename = 'event.ics'
textData = Data.fromString(calEventString)
wrapper = FileWrapper.withContents(filename, textData)
folderURL = URL.documentsDirectory
fileURL = folderURL.appendingPathComponent(filename)
wrapper.write(
fileURL,
[FileWrapper.WritingOptions.Atomic],
null
)
if (Device.current.mac){
fileURL.open()
}
}
catch(err){
if(!err.causedByUserCancelling){
new Alert(err.name, err.message).show()
}
}
});
action.validate = function(selection, sender){
return (selection.tasks.length === 1 && selection.tasks[0].dueDate != null)
};
return action;
})();