Plug-In: Task Date Controls

This plug-in provides the following actions for changing defer and/or due dates on selected tasks.

(AUTHOR: Christian Y. · Omni Support Human)
Screenshot

Several of the +1 actions will appear to overlap with functionality already provide in the Dates inspector, but they function differently. Even when using these +1 date actions with multiple actions selected, the date for each task will be adjusted relative to its existing date. Additionally, when using Defer Tomorrow or Due Today on tasks that have an existing defer or due date, the time of day for the existing date will be preserved.

The Set New Dates action then provides the ability to apply the same defer and/or due to a selection of multiple tasks. This too will seem similar to just using the Date inspector, but again there are some key differences. One is that you can choose to only update the date on tasks, while preserving the time of day. The other is it allows you the option to update the defer date on a task, and automatically push out the due date to keep the same relative gap between the defer date and due date.

Installation

You can download the plug-in to disk (Download Plug-In), or begin the OmniFocus plug-in onboarding process (Install Plug-In)

 

Using the Plug-In

There are four different ways to use the actions provided by this plug-in (available when at least one task is selected):

Return to: OmniFocus Plug-In Collection

Plug-In Scripts

Shown here are three of the scripts contained in this plug-in.

First, is the bundle plug-in library file that is shared by all the other scripts for the individual actions.

Plug-In's DateLib Library


(() => { let DateLib = new PlugIn.Library(new Version("1.0")); DateLib.getSelection = () => document.windows[0].selection DateLib.today = Calendar.current.startOfDay(new Date()) DateLib.getTimeComponentsFromString = function(timeString) { let placeholderDate = new Date("1/1/1 " + timeString) let defaultTimeComponents = new DateComponents() defaultTimeComponents.hour = placeholderDate.getHours() defaultTimeComponents.minute = placeholderDate.getMinutes() return defaultTimeComponents } DateLib.getDefaultDeferTimeComponents = function() { return this.getTimeComponentsFromString(settings.objectForKey("DefaultStartTime")) } DateLib.getDefaultDueTimeComponents = function() { return this.getTimeComponentsFromString(settings.objectForKey("DefaultDueTime")) } DateLib.incrementDate = function(dateType, dateIncrementer) { let sel = this.getSelection() let cal = Calendar.current let defaultTimeComponents sel.databaseObjects.forEach(obj => { if (obj instanceof Task || obj instanceof Project){ let oldDate let newDate if (dateType == "due") { oldDate = obj.dueDate defaultTimeComponents = this.getDefaultDueTimeComponents() } else if (dateType == "defer") { oldDate = obj.deferDate defaultTimeComponents = this.getDefaultDeferTimeComponents() } // Apply a new date to items that don't have an existing date if (!oldDate) { if (dateIncrementer.hour) { oldDate = new Date() } else { oldDate = cal.dateByAddingDateComponents(new Date(this.today), defaultTimeComponents) } } // Set due date to today or defer date to tomorrow if (dateIncrementer.day === 0) { let oldHour = oldDate.getHours() let oldMinutes = oldDate.getMinutes() // Reset old date to today, but preserve the time oldDate = new Date(this.today) oldDate.setHours(oldHour) oldDate.setMinutes(oldMinutes) if (dateType == "defer") {dateIncrementer.day = 1} } newDate = cal.dateByAddingDateComponents(oldDate, dateIncrementer) if (dateType == "due") obj.dueDate = newDate if (dateType == "defer") obj.deferDate = newDate } }) } DateLib.clearDate = function(dateField){ let sel = this.getSelection() sel.databaseObjects.forEach(obj => { if (obj instanceof Task || obj instanceof Project){ if (obj.task != undefined) {obj = obj.task} // workaround for bug setting dueDate to null on Project objects if (dateField.includes("due")) {obj.dueDate = null} if (dateField.includes("defer")) {obj.deferDate = null} } }) } return DateLib; })(); (() => { let action = new PlugIn.Action(function(selection, sender){ let dc = new DateComponents(); dc.day = 1 this.DateLib.incrementDate("defer", dc) }) action.validate = function(selection, sender){ return (selection.projects.length > 0 || selection.tasks.length > 0) }; return action; })();

Then, there is the Due +1 Day script, which shows the basic structure used by all the other action scripts, except for Set New Dates, which is the last script shown. The Set New Dates script is largely self-contained though it does use the plug-ins library file in a couple of spots.

Due +1 Day


(() => { let action = new PlugIn.Action(function(selection, sender){ let dc = new DateComponents(); dc.day = 1 this.DateLib.incrementDate("due", dc) }) action.validate = function(selection, sender){ return (selection.projects.length > 0 || selection.tasks.length > 0) }; return action; })();
Set New Dates


(() => { let action = new PlugIn.Action(function(selection) { // Add code to run when the action is invoked let cal = Calendar.current let DateLib = this.DateLib let optionInputField = new Form.Field.Option( "dateOption", "Dates to update", [0,1,2], ["Defer Only", "Due Only", "Defer & Due"], 1 ) let deferInputField = function() { let form = new Form.Field.Date( "deferInput", "Defer", new Date(), Formatter.Date.withFormat("MM/dd/yyyy") ) return form } let deferInputWithTimeField = function() { let form = new Form.Field.Date( "deferInputWithTime", "Defer", new Date(), null ) return form } let dueInputField = function() { let form = new Form.Field.Date( "dueInput", "Due", new Date(), Formatter.Date.withFormat("MM/dd/yyyy") ) return form } let dueInputWithTimeField = function() { let form = new Form.Field.Date( "dueInputWithTime", "Due", new Date(), null ) return form } let pushDueCheckbox = function() { let form = new Form.Field.Checkbox( "pushDue", "Push out due date relative to new defer date" ) return form } let includeTimeCheckbox = new Form.Field.Checkbox( "includeTime", "Adjust time on dates" ) let inputForm = new Form() let formPrompt = "Batch Update Dates:" let buttonTitle = "Continue" inputForm.addField(optionInputField) inputForm.addField(dueInputField()) inputForm.addField(includeTimeCheckbox) let formPromise = inputForm.show(formPrompt, buttonTitle) inputForm.validate = function(formObject) { let dateOption = formObject.values["dateOption"] let includeTime = formObject.values["includeTime"] let deferInput = formObject.values["deferInput"] let deferInputWithTime = formObject.values["deferInputWithTime"] let dueInput = formObject.values["dueInput"] let dueInputWithTime = formObject.values["dueInputWithTime"] let dateFilled // Set up the form for "Defer Only" if (dateOption == 0) { // Remove the second date field when switching from "Defer & Due" if (inputForm.fields[2]["key"].includes("due")) { inputForm.removeField(inputForm.fields[2]) } if (inputForm.fields[2]["key"] != "pushDue") { inputForm.addField(pushDueCheckbox(), 2) } if (!includeTime) { if (inputForm.fields[1]["key"] != "deferInput") { inputForm.removeField(inputForm.fields[1]) inputForm.addField(deferInputField(), 1) } dateFilled = deferInput } else if (includeTime) { if (inputForm.fields[1]["key"] != "deferInputWithTime") { inputForm.removeField(inputForm.fields[1]) inputForm.addField(deferInputWithTimeField(), 1) } dateFilled = deferInputWithTime } } // Set up the form for "Due Only" else if (dateOption == 1) { if (inputForm.fields.length == 4) { // Remove the extra form field when switching from the other dateOptions inputForm.removeField(inputForm.fields[2]) } if (!includeTime) { if (inputForm.fields[1]["key"] != "dueInput") { inputForm.removeField(inputForm.fields[1]) inputForm.addField(dueInputField(), 1) } dateFilled = dueInput } else if (includeTime) { if (inputForm.fields[1]["key"] != "dueInputWithTime") { inputForm.removeField(inputForm.fields[1]) inputForm.addField(dueInputWithTimeField(), 1) } dateFilled = dueInputWithTime } } // Set up the form for "Defer & Due" else if (dateOption == 2) { if (inputForm.fields.length == 3) { inputForm.addField(deferInputField(), 1) } if (!includeTime) { if (inputForm.fields[1]["key"] != "deferInput" || inputForm.fields[2]["key"] != "dueInput") { inputForm.removeField(inputForm.fields[1]) inputForm.addField(deferInputField(), 1) inputForm.removeField(inputForm.fields[2]) inputForm.addField(dueInputField(), 2) } dateFilled = (deferInput && dueInput) } else if (includeTime) { if (inputForm.fields[1]["key"] != "deferInputWithTime" || inputForm.fields[2]["key"] != "dueInputWithTime") { inputForm.removeField(inputForm.fields[1]) inputForm.addField(deferInputWithTimeField(), 1) inputForm.removeField(inputForm.fields[2]) inputForm.addField(dueInputWithTimeField(), 2) } dateFilled = (deferInputWithTime && dueInputWithTime) } } // Make sure all date fields (per the selected dateOption) are filled in if (dateFilled){return true} } formPromise.then(function(formObject) { let dateOption = formObject.values["dateOption"] let updateDateOnly = !formObject.values["includeTime"] let pushDue = formObject.values["pushDue"] let newDeferDate let newDueDate // sel = document.windows[0].selection selection.databaseObjects.forEach(function(obj) { if (obj instanceof Task || obj instanceof Project) { let oldDeferDate = obj.deferDate let oldDueDate = obj.dueDate // Defer Only if (dateOption == 0 || dateOption == 2) { if (updateDateOnly) { newDeferDate = formObject.values["deferInput"] if (oldDeferDate) { newDeferDate.setHours(oldDeferDate.getHours()) newDeferDate.setMinutes(oldDeferDate.getMinutes()) } else { let defaultDeferDate = DateLib.getDefaultDeferTimeComponents() newDeferDate.setHours(defaultDeferDate.hour) newDeferDate.setMinutes(defaultDeferDate.minute) } } else { newDeferDate = formObject.values["deferInputWithTime"] } obj.deferDate = newDeferDate // Push out due date relative to new defer date if (pushDue) { let dateDiff = cal.dateComponentsBetweenDates(oldDeferDate, oldDueDate) newDueDate = cal.dateByAddingDateComponents(newDeferDate, dateDiff) obj.dueDate = newDueDate } } // Due Only if (dateOption == 1 || dateOption == 2) { if (updateDateOnly) { newDueDate = formObject.values["dueInput"] if (oldDueDate) { newDueDate.setHours(oldDueDate.getHours()) newDueDate.setMinutes(oldDueDate.getMinutes()) } else { let defaultDueDate = DateLib.getDefaultDueTimeComponents() newDueDate.setHours(defaultDueDate.hour) newDueDate.setMinutes(defaultDueDate.minute) } } else { newDueDate = formObject.values["dueInputWithTime"] } obj.dueDate = newDueDate } } }) }) }); action.validate = function(selection, sender) { return (selection.projects.length > 0 || selection.tasks.length > 0) }; return action; })();