×

Database

The heart of OmniFocus is a powerful database that stores and manages your personal tasks and schedule. The database is stored in a file that OmniFocus uses to hold all of the information that you add to the app. OmniFocus and its various perspectives act as windows onto your database, interpreting the data there in ways that help you get stuff done.

Typically, you’ll only interact with your primary default OmniFocus database; you can open multiple windows onto it at once to maintain different view states simultaneously. More rarely, you can open multiple database files at once to restore from a backup or archive, or view the contents of an OmniFocus database other than your own.

This topic page describes the properties and functions of the Database class, demonstrating how they are used to access and automate the processing of database data.

The Database defines the top-level global context for a JavaScript session (i.e., globalThis). Every function and property defined at this level can be referenced in code by simply referring to its name (e.g. library or inbox or moveTasks()).

Database Object Arrays

The component elements of the OmniFocus database, such as instances of the Tag, Task, Project, Folder classes, are often referenced as standard JavaScript arrays. You will find that the value of some of the properties of the Database class are returned as a specific subclass of Array.

For example, the value of the flattenedTags property of the Database class is an instance of the TagArray subclass of the standard JavaScript Array class.

These specialized array classes may have properties and functions associated with them. For reference, here are the array subclasses used when referencing database objects:

FolderArray
An Array containing Folder objects.
ProjectArray
An Array containing Project objects.
SectionArray
An Array containing Project and Folder objects.
TagArray
An Array containing Tag objects.
TaskArray
An Array containing Task objects.

Database Properties

Here are the scripting properties of the database:

NOTE: The Inbox, Library, and Tags classes that are the values of the their related Database class properties (inbox, library, tags) share the same set of array position indicator properties:

Inbox class:

Library class:

Tags class:

Using the positional insertion indicators:

Make New Items at Locations


new Task("My New Task"", inbox.beginning) new Project("My New Project", library.ending) new Tag("My New Tag", tags.beginning)

In addition, the three Array classes (Inbox, Library, and Tags) also support the use of the apply(…) function for iterating their object heirarchies, which is described in detail on the Finding Items topic page.

Iterate All Tasks in Inbox Hierarchy


inbox.apply(task => { if (task.taskStatus === Task.Status.Available){ // processing statements go here } })

The “flattened” Database Properties

Using the “flattened” properties of the Database class (flattenedFolders, flattenedProjects, flattenedTasks, flattenedTags) scripts can iterate all instances of the specified class in the entire database. For example, the following script iterates every task in the database:

Iterate All Tasks in Database


flattenedTasks.forEach(task => { if (task.taskStatus === Task.Status.Available){ // processing statements go here } })

Here's an example of using the “flattened” properties with the JavaScript filter() method to generate an array of references to all tasks whose status is available:

Filter All Tasks in Database


var matches = flattenedTasks.filter(item => { return item.taskStatus === Task.Status.Available })

And a variation of the previous script that filters all tasks for those that are available, and then processes each of the matched tasks:

Filter and Process Matched Taskss


flattenedTasks.filter(item => { return item.taskStatus === Task.Status.Available }).forEach(task => { // processing statements })

And here is a script example that filters and processes all tasks whose name begins with a specific word. (NOTE: The optional use of the toUpperCase() function is to ensure the string comparision is case-insensitive.)

Filter and Process All Tasks whose Name begins with Specified String


targetString = "three" flattenedTasks.filter(item => { return item.name.toUpperCase().startsWith(targetString.toUpperCase()) }).forEach(task => { // processing statements })

Database Functions

Here are the scripting functions of the Database class:

Paste Tasks from Clipboard


if(canPasteTasks(Pasteboard.general)){ var win = document.windows[0] win.perspective = Perspective.BuiltIn.Inbox var pastedTasks = pasteTasksFromPasteboard() win.selectObjects(pastedTasks) }

Here’s an example script that references an Inbox task by name and creates a new task if the task does not exist at the top-level:

Reference Inbox Task by Name (create if needed)


task = taskNamed("My Task") || new Task("My Task") //--> [object Task: My Task]

And a version of the previous script that searches the entire database for an existing task by name:

Reference Task by Name (create if needed)


task = flattenedTasks.byName("My Task") || new Task("My Task") //--> [object Task: My Task]

Here is an exampale of moving one or more tags:

Move Tag to the End of Tags


moveTags([tagNamed('Northeast')],tags.ending)

Here's a script that duplicates the selected tasks to the inbox:

Duplicate Selected Tasks to Inbox


var win = document.windows[0] var tasks = win.selection.tasks if (tasks.length > 0){ duplicateTasks(tasks, inbox.beginning) win.perspective = Perspective.BuiltIn.Inbox }

Here’s a script that performs a database redo() function:

Redo


if (canRedo){redo()}

Here’s a script that uses the deleteObject() method to remove all items from the Inbox:

Clear Inbox


inbox.forEach(tsk => deleteObject(tsk))

Here’s a script that uses the deleteObject() method to remove all items from the database Projects directory:

Clear Projects


projects.forEach(project => deleteObject(project))

Here's an example using the generalized smart matching commands to find all tags matching “Auto-”:

Matched Searches
 

tagsMatching("Auto-") //--> [[object Tag: Auto-Open], [object Tag: Auto-Close], [object Tag: Auto-Update]]

Special Pasteboard Functions

The Database class includes special functions for copying and pasting tasks using the Pasteboard class:

omnifocus://localhost/omnijs-run?script=try%7Bvar%20tasks%20%3D%20document%2Ewindows%5B0%5D%2Eselection%2Etasks%0Aif%20%28tasks%2Elength%20%3E%200%29%7B%0A%09var%20clipboard%20%3D%20Pasteboard%2EmakeUnique%28%29%0A%09copyTasksToPasteboard%28tasks%2C%20clipboard%29%0A%09var%20duplicatedTasks%20%3D%20pasteTasksFromPasteboard%28clipboard%29%0A%09document%2Ewindows%5B0%5D%2Eperspective%20%3D%20Perspective%2EBuiltIn%2EInbox%0A%09moveTasks%28duplicatedTasks%2Cinbox%2Ebeginning%29%0A%7D%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
Duplicate Selected Tasks to Inbox
 

var win = document.windows[0] var tasks = win.selection.tasks if (tasks.length > 0){ var clipboard = Pasteboard.makeUnique() copyTasksToPasteboard(tasks, clipboard) var duplicatedTasks = pasteTasksFromPasteboard(clipboard) win.perspective = Perspective.BuiltIn.Inbox moveTasks(duplicatedTasks,inbox.beginning) }

And here's a script example that will export all of the tasks of a specified project to a single TaskPaper file on disk:

Export Project Tasks to TaskPaper File
 

var project = flattenedProjects.byName("Target Project") if(project){ copyTasksToPasteboard(project.flattenedTasks, Pasteboard.general) var data = Data.fromString(Pasteboard.general.string) var wrapper = FileWrapper.withContents('Exported Tasks.taskpaper', data) var filesaver = new FileSaver() var fileSaverPromise = filesaver.show(wrapper) fileSaverPromise.then(urlObj => { console.log(urlObj.string) urlObj.open() }) fileSaverPromise.catch(err => { console.error(err.message) }) }
omnifocus://localhost/omnijs-run?script=try%7Bvar%20TaskpaperText%20%3D%20%60%2D%20PROJECT%20C%20%40parallel%28true%29%20%40autodone%28false%29%20%40due%28tomorrow%29%0A%09Project%20notes%2E%0A%09%2D%20TASK%20E%20%40parallel%28true%29%20%40autodone%28false%29%0A%09%2D%20TASK%20F%20%40parallel%28true%29%20%40autodone%28false%29%60%0APasteboard%2Egeneral%2Estring%20%3D%20TaskpaperText%0Avar%20newItems%20%3D%20pasteTasksFromPasteboard%28Pasteboard%2Egeneral%29%0Adocument%2Ewindows%5B0%5D%2Eperspective%20%3D%20Perspective%2EBuiltIn%2EInbox%0Adocument%2Ewindows%5B0%5D%2EselectObjects%28newItems%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D
Paste TaskPaper Content
 

var TaskPaperText = `- PROJECT C @parallel(true) @autodone(false) @due(tomorrow) Project notes. - TASK E @parallel(true) @autodone(false) - TASK F @parallel(true) @autodone(false)` Pasteboard.general.string = TaskPaperText var newItems = pasteTasksFromPasteboard(Pasteboard.general) document.windows[0].perspective = Perspective.BuiltIn.Inbox document.windows[0].selectObjects(newItems)

Here’s an example script that will log the selected tasks to the console as TaskPaper content:

Log Selected Tasks as TaskPaper


(async () => { try { var selection = document.windows[0].selection if(!selection.tasks.length > 0){ throw { name: "Selection Issue", message: "Please select one or more tasks." } } var tasks = selection.tasks var clipboard = Pasteboard.makeUnique() copyTasksToPasteboard(tasks, clipboard) var data = Data.fromString(clipboard.string) var wrapper = FileWrapper.withContents('Pasteboard.txt', data) console.log( wrapper.contents.toString()) } catch(err) { var alertPromise = await new Alert(err.name, err.message).show() throw `${err.name}\n${err.message}` } })();

The convertTasksToProjects() Function

For example, to convert each top-level inbox item into a new project at the end of your library and capture the resulting projects:

Convert Inbox Tasks to Projects


var newProjects = convertTasksToProjects(inbox, library.ending)

An example plug-in for converting selected tasks into projects can be found here.

Plug-In Examples

The following plug-ins incorporate the use of properties and functions of the Database class combined with Action Forms to automate multi-step processes with the database:

Move Selected Projects into New Folder
 

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.move-selected-projects-into-folder", "version": "1.0", "description": "Move the selected projects into a new top-level folder.", "label": "Move Selected Projects into New Folder", "shortLabel": "Move Projects" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // selection options: tasks, projects, folders, tags // CONSTRUCT THE FORM var inputForm = new Form() // CREATE FORM ELEMENTS: TEXT INPUT textField = new Form.Field.String("folderName", null, null) // ADD THE ELEMENTS TO THE FORM inputForm.addField(textField) // DIALOG PROMPT AND OK BUTTON TITLE let formPrompt = "Enter the name for the new top-level folder:" 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['folderName'] return ((textValue) ? true:false) } // PERFORM PROCESSES USING FORM DATA formPromise.then(function(formObject){ textValue = formObject.values['folderName'] folder = new Folder(textValue) moveSections(selection.projects, folder) // SHOW THE FOLDER fldID = folder.id.primaryKey urlStr = "omnifocus:///folder/" + fldID URL.fromString(urlStr).open() }) // PROCESS FORM CANCELLATION formPromise.catch(function(error){ console.log("form cancelled", error) }) }); action.validate = function(selection, sender){ // selection options: tasks, projects, folders, tags return (selection.projects.length > 0) }; return action; })();
Move Selected Tasks into New Project
 

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.move-selected-tasks-into-project", "version": "1.1", "description": "Move the selected tasks into a new top-level project.", "label": "Move Selected Tasks into New Project", "shortLabel": "Move Tasks" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // selection options: tasks, projects, folders, tags // CONSTRUCT THE FORM var 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) // DIALOG PROMPT AND OK BUTTON TITLE let formPrompt = "Enter the name for the new top-level project and select its project type:" 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['projectName'] return ((textValue) ? true:false) } // PERFORM PROCESSES USING FORM DATA formPromise.then(function(formObject){ 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() }) // PROCESS FORM CANCELLATION formPromise.catch(function(err){ console.log("form cancelled", err.message) }) }); action.validate = function(selection, sender){ // selection options: tasks, projects, folders, tags 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.move-selected-tasks-into-new-action", "version": "1.0", "description": "Move the selected tasks into a new top-level action group.", "label": "Move Selected Tasks into New Action Group", "shortLabel": "Move Tasks" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // selection options: tasks, projects, folders, tags // CONSTRUCT THE FORM var 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) // DIALOG PROMPT AND OK BUTTON TITLE let formPrompt = "Enter the name for the new top-level action group and select its type:" 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['groupName'] return ((textValue) ? true:false) } // PERFORM PROCESSES USING FORM DATA formPromise.then(function(formObject){ 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() }) // PROCESS FORM CANCELLATION formPromise.catch(function(err){ console.log("form cancelled", err.message) }) }); action.validate = function(selection, sender){ // selection options: tasks, projects, folders, tags return (selection.tasks.length > 0) }; return action; })();
Duplicate Selected Tasks
 

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of.duplicate-selected-tasks", "version": "1.0", "description": "This action will duplicate the selected tasks in their parent container.", "label": "Duplicate Selected Tasks", "shortLabel": "Duplicate" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // action code // selection options: tasks, projects, folders, tags var duplicatedTasks = new Array() selection.tasks.forEach(function(task){ insertionLocation = task.containingProject if(insertionLocation === null){insertionLocation = inbox.ending} dupTasks = duplicateTasks([task], insertionLocation) duplicatedTasks.push(dupTasks[0].id.primaryKey) }) idStr = duplicatedTasks.join(",") URL.fromString("omnifocus:///task/" + idStr).open() }); action.validate = function(selection, sender){ // validation code // selection options: tasks, projects, folders, tags return (selection.tasks.length > 0) }; return action; })();
Duplicate Selected Tasks and Set New Due Date
 

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of.dup-sel-tasks-set-due-date", "version": "1.0", "description": "This action will duplicate the selected tasks in their parent container, and assign the user-provided date as the due date.", "label": "Duplicate Selected Tasks with New Due Date", "shortLabel": "Duplicate with Due Date" }*/ (() => { var action = new PlugIn.Action(function(selection, sender){ // action code // selection options: tasks, projects, folders, tags today = new Date(new Date().setHours(0,0,0,0)) var tomorrow = new Date(today.setDate(today.getDate() + 1)) dateInputField = new Form.Field.Date( "dateInput", null, null ) inputForm = new Form() inputForm.addField(dateInputField) formPrompt = "Enter a due date/time after today:" buttonTitle = "Continue" formPromise = inputForm.show(formPrompt, buttonTitle) inputForm.validate = function(formObject){ dateInput = formObject.values["dateInput"] var status = (dateInput && dateInput >= tomorrow) ? true:false console.log(status) return status } formPromise.then(function(formObject){ var dateInput = formObject.values["dateInput"] console.log(dateInput) var duplicatedTasks = new Array() selection.tasks.forEach(function(task){ insertionLocation = task.containingProject if(insertionLocation === null){insertionLocation = inbox.ending} dupTasks = duplicateTasks([task], insertionLocation) dupTask = dupTasks[0] dupTask.dueDate = dateInput duplicatedTasks.push(dupTask.id.primaryKey) }) idStr = duplicatedTasks.join(",") URL.fromString("omnifocus:///task/" + idStr).open() }) formPromise.catch(function(error){ console.log("form cancelled", error.message) }) }); action.validate = function(selection, sender){ // validation code // selection options: tasks, projects, folders, tags return (selection.tasks.length > 0) }; return action; })();