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.

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:

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.

inbox.apply((task)=>{ if (task.taskStatus === Task.Status.Available){ // processing statements go here } })
flattenedTasks.forEach(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:

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:

matches = flattenedTasks.filter(function(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:

flattenedTasks.filter(function(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.)

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

Database Functions

Here are the scripting functions of the Database class:

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:

task = taskNamed("My Task") || new Task("My Task") //--> [object Task: My Task]
omnifocus://localhost/omnijs-run?script=try%7Btask%20%3D%20taskNamed%28%22My%20Task%22%29%20%7C%7C%20new%20Task%28%22My%20Task%22%29%0Aconsole%2Elog%28task%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D

Here is an exampale of moving one or more tags:

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

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

var tasks = document.windows[0].selection.tasks if (tasks.length > 0){ duplicateTasks(tasks,inbox.beginning) document.windows[0].perspective = Perspective.BuiltIn.Inbox }
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%09duplicateTasks%28tasks%2Cinbox%2Ebeginning%29%0A%09document%2Ewindows%5B0%5D%2Eperspective%20%3D%20Perspective%2EBuiltIn%2EInbox%0A%7D%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D

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

if (canRedo) {redo()}
omnifocus://localhost/omnijs-run?script=try%7Bif%20%28canRedo%29%20%7Bredo%28%29%7D%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D

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

inbox.forEach(tsk => deleteObject(tsk))
omnifocus://localhost/omnijs-run?script=try%7Binbox%2EforEach%28%28tsk%29%20%3D%3E%20%7BdeleteObject%28tsk%29%7D%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D

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

projects.forEach(project => deleteObject(project))
omnifocus://localhost/omnijs-run?script=try%7Bprojects%2EforEach%28project%20%3D%3E%20deleteObject%28project%29%29%7Dcatch%28err%29%7Bconsole%2Elog%28err%29%7D

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

Special Pasteboard Functions

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

var tasks = document.windows[0].selection.tasks if (tasks.length > 0){ var clipboard = Pasteboard.makeUnique() copyTasksToPasteboard(tasks, clipboard) var duplicatedTasks = pasteTasksFromPasteboard(clipboard) document.windows[0].perspective = Perspective.BuiltIn.Inbox moveTasks(duplicatedTasks,inbox.beginning) }
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
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)
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

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:

/*{ "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).call(reply => {}) }) // 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; })();
/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.move-selected-tasks-into-project", "version": "1.0", "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).call(reply => {}) }) // 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.tasks.length > 0) }; return action; })();
/*{ "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).call(reply => {}) }) // 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; })();
/*{ "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; })();
/*{ "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; })();

DISCLAIMER