OmniFocus: Task Status Board

Here’s an example shortcut that demonstrates the powerful interaction between Omni Automation and Shortcuts: using the best of both technologies to deliver solutions greater than either could deliver on their own.

This shortcut will create and display, alongside the current OmniFocus window, an HTML-based page showing a 3-column task review based upon the use of three specified tags.

IMPORTANT: On iOS and iPadOS most browsers are blocked from opening HTML files. The iCab Mobile (Web Browser) application by Alexander Clauss appears to be an exception, and displays the shortcut-generated task status table HTML file. On iOS/iPadOS you will need to adjust the last two actions in the shortcut to use this application.

DOWNLOAD SHORTCUT

Video 1: Task Status Board
Shortcut creates and displays, alongside the current OmniFocus window, an HTML-based page showing a 3-column task review based upon the use of three specified tags.

 1  Tag Titles • A dictionary containing the titles of the three status tags. NOTE: edit the values of the dictionary items to match the tags you wish to use.

 2  Opening HTML • The HTML code is divided into two sections: the opening, and the closing. This is the opening section with text placeholders for the tag titles inserted into the HTML (without quotes): “XXXXX” “YYYYY” “ZZZZZ”

 3, 4, 5  Replace Placeholders • Replace the text placeholders with the specified titles of the tags.

 4-5  Closing HTML and Variable • The contents of the closing HTML is stored in a variable.

 6  Variable • The opening HTML, including the text replacements, is stored in a variable.

Shortcuts Workflow

 9  Dictionary • The contents of the opening and closing HTML sections, as well as the tag titles, are placed in a dictionary to be accessed by the Omni Automation script.

 10  Data and Script • The dictionary is placed in the Data Input Socket of the “Omni Automation Script” action, and is accessed within the script by using the argument.input parameter. The script dynamically generates HTML rows for the tasks tagged in OmniFocus with the either of the three specified tags.

 11  Name the File • Name the HTML generated by the previous action: “status.html”

 12  Export to File • Save the named content to file on disk placed within the Shortcuts sandbox.

 13  Open File • Open the HTML file in Safari. IMPORTANT: On iPadOS/iOS, the opening of HTML files by most browsers is blocked and so this action will trigger an error. See the notice at the top of the page regarding compatible browser applications.

 14  Split View • Display a split view with OmniFocus and the browser (Safari) displaying the generated task status board content.

Processing Omni Automation Script

Here is the script that generates the custom content, wrapped within the HTML opening and closing content.

Generate HTML Status Page


(async () => { try { tag1Title = argument.input.tagTitle1 tag2Title = argument.input.tagTitle2 tag3Title = argument.input.tagTitle3 tag1 = flattenedTags.byName(tag1Title) || new Tag(tag1Title) tag2 = flattenedTags.byName(tag2Title) || new Tag(tag2Title) tag3 = flattenedTags.byName(tag3Title) || new Tag(tag3Title) items1 = tag1.tasks.filter(task => { return task.taskStatus === Task.Status.Available }) items2 = tag2.tasks.filter(task => { return task.taskStatus === Task.Status.Available }) items3 = tag3.tasks.filter(task => { return task.taskStatus === Task.Status.Available }) lengths = [items1.length, items2.length, items3.length] if(lengths === [0, 0, 0]){ throw { name:"No Matches", message:`No tasks have been tagged with: ${tag3Title}, ${tag2Title}, or ${tag3Title}` } } loopCount = Math.max.apply(null, lengths) row = "\t\t\nXXXX\n\t\t" cell = "\t\t\tXXXX" rows = [] for (index = 0; index < loopCount; index++) { item = items1[index] if (typeof item === "undefined"){cell1 = ""} else { title = item.name link = `omnifocus://task/${item.id.primaryKey}` // cell1 = cell.replace("XXXX",`<a href="${link}">${title}</a>`) cell1 = "<td>" + `<a href="${link}">${title}</a>` + "</td>" } item = items2[index] if (typeof item === "undefined"){cell2 = ""} else { title = item.name link = `omnifocus://task/${item.id.primaryKey}` // cell2 = cell.replace("XXXX",`<a href="${link}">${title}</a>`) cell2 = "<td>" + `<a href="${link}">${title}</a>` + "</td>" } item = items3[index] if (typeof item === "undefined"){cell3 = ""} else { title = item.name link = `omnifocus://task/${item.id.primaryKey}` // cell3 = cell.replace("XXXX",`<a href="${link}">${title}</a>`) cell3 = "<td>" + `<a href="${link}">${title}</a>` + "</td>" } rowContent = "\t\t\t" + cell1 + "\n" + "\t\t\t" + cell2 + "\n" + "\t\t\t" + cell3 rowContent = row.replace("XXXX", rowContent) rows.push(rowContent) } return (argument.input.opening + "\n" + rows.join("\n") + "\n" + argument.input.closing) } catch(err){ if(!err.message.includes("cancelled")){ await new Alert(err.name, err.message).show() } throw `${err.name}\n${err.message}` } })();

Assign Tag Script

The following script iterates the selected tasks and assigns a specified tag of a tag set to each task, removing any existing tags from the tag set.

Assign Tag of Tag Set


allTags = flattenedTags tagTitles = ["XXXXX", "YYYYY", "ZZZZZ"] tagSet = new Array() tagTitles.forEach(item => { tag = allTags.byName(item) || new Tag(item) tagSet.push(tag) }) replacementTag = flattenedTags.byName("TTTTT") document.windows[0].selection.tasks.forEach(task => { task.removeTags(tagSet) task.addTag(replacementTag) })

And here is the script converted into an Omni Automation script URL that is edited and used with the column titles of the task status table to enable selected tasks to be assigned tags by clicking or tapping the column title link.

Encoded Assignment Script


omnifocus://localhost/omnijs-run?script=allTags%20%3D%20flattenedTags%0AtagTitles%20%3D%20%5B%22XXXXX%22%2C%20%22YYYYY%22%2C%20%22ZZZZZ%22%5D%0AtagSet%20%3D%20new%20Array%28%29%0AtagTitles%2EforEach%28item%20%3D%3E%20%7B%0A%09tag%20%3D%20allTags%2EbyName%28item%29%20%7C%7C%20new%20Tag%28item%29%0A%09tagSet%2Epush%28tag%29%0A%7D%29%0AreplacementTag%20%3D%20flattenedTags%2EbyName%28%22TTTTT%22%29%0Adocument%2Ewindows%5B0%5D%2Eselection%2Etasks%2EforEach%28task%20%3D%3E%20%7B%0A%09task%2EremoveTags%28tagSet%29%0A%09task%2EaddTag%28replacementTag%29%0A%7D%29

The placeholder TTTTT is replaced by the title of the tag from the tag set that is to be assigned to the selected tasks.

Task Status Table HTML

For reference, here is the HTML code for the Task Status Table webpage:

Task Status Table HTML


<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>Status View</title> <style> body {background-color: #323232;} #status-table { font-size: normal; color: black; font-family: -apple-system, Helvetica, sans-serif; font-style: normal; width: 100%; border-collapse: collapse; } #status-table a {text-decoration: none;} #status-table tr td { padding: 8px; border-radius: 12px; border: 2px solid #000; } #instructions { color:white; margin-top:36px; margin-bottom: 36px; text-align: center; font-family: -apple-system, Helvetica, sans-serif; font-size: 85%; line-height: 100%; } </style> </head> <body> <p id="instructions">TAP|CLICK column title to assign tag to selected task:</p> <table id="status-table" style="margin-top:36px;"> <col style="background-color: #FAFFC7;" /> <col style="background-color: #FFCCE1;" /> <col style="background-color: #D7EEFF;" /> <tr> <td style="font-size:150%;"> <a style="text-decoration:none;" href="omnifocus://localhost/omnijs-run?script=allTags%20%3D%20flattenedTags%0AtagTitles%20%3D%20%5B%22XXXXX%22%2C%20%22YYYYY%22%2C%20%22ZZZZZ%22%5D%0AtagSet%20%3D%20new%20Array%28%29%0AtagTitles%2EforEach%28item%20%3D%3E%20%7B%0A%09tag%20%3D%20allTags%2EbyName%28item%29%20%7C%7C%20new%20Tag%28item%29%0A%09tagSet%2Epush%28tag%29%0A%7D%29%0AreplacementTag%20%3D%20flattenedTags%2EbyName%28%22XXXXX%22%29%0Adocument%2Ewindows%5B0%5D%2Eselection%2Etasks%2EforEach%28task%20%3D%3E%20%7B%0A%09task%2EremoveTags%28tagSet%29%0A%09task%2EaddTag%28replacementTag%29%0A%7D%29">XXXXX</a> </td> <td style="font-size:150%;"> <a style="text-decoration:none;" href="omnifocus://localhost/omnijs-run?script=allTags%20%3D%20flattenedTags%0AtagTitles%20%3D%20%5B%22XXXXX%22%2C%20%22YYYYY%22%2C%20%22ZZZZZ%22%5D%0AtagSet%20%3D%20new%20Array%28%29%0AtagTitles%2EforEach%28item%20%3D%3E%20%7B%0A%09tag%20%3D%20allTags%2EbyName%28item%29%20%7C%7C%20new%20Tag%28item%29%0A%09tagSet%2Epush%28tag%29%0A%7D%29%0AreplacementTag%20%3D%20flattenedTags%2EbyName%28%22YYYYY%22%29%0Adocument%2Ewindows%5B0%5D%2Eselection%2Etasks%2EforEach%28task%20%3D%3E%20%7B%0A%09task%2EremoveTags%28tagSet%29%0A%09task%2EaddTag%28replacementTag%29%0A%7D%29">YYYYY</a> </td> <td style="font-size:150%;"> <a style="text-decoration:none;" href="omnifocus://localhost/omnijs-run?script=allTags%20%3D%20flattenedTags%0AtagTitles%20%3D%20%5B%22XXXXX%22%2C%20%22YYYYY%22%2C%20%22ZZZZZ%22%5D%0AtagSet%20%3D%20new%20Array%28%29%0AtagTitles%2EforEach%28item%20%3D%3E%20%7B%0A%09tag%20%3D%20allTags%2EbyName%28item%29%20%7C%7C%20new%20Tag%28item%29%0A%09tagSet%2Epush%28tag%29%0A%7D%29%0AreplacementTag%20%3D%20flattenedTags%2EbyName%28%22ZZZZZ%22%29%0Adocument%2Ewindows%5B0%5D%2Eselection%2Etasks%2EforEach%28task%20%3D%3E%20%7B%0A%09task%2EremoveTags%28tagSet%29%0A%09task%2EaddTag%28replacementTag%29%0A%7D%29">ZZZZZ</a> </td> </tr> <!-- GENERATED ROWS GO HERE --> </table> </body> </html>

Demo Setup Script

Here’s a script for creating demo content to try the shortcut:

omnifocus://localhost/omnijs-run?script=currentTags%20%3D%20flattenedTags%0AtagTitles%20%3D%20%5B%22To%20Do%22%2C%20%22Doing%22%2C%20%22Done%22%5D%0AtagTitles%2EforEach%28tagName%20%3D%3E%20%7B%0A%09currentTags%2EbyName%28tagName%29%20%7C%7C%20new%20Tag%28tagName%29%0A%7D%29%0AtaskNames%20%3D%20%5B%22One%22%2C%20%22Two%22%2C%20%22Three%22%2C%20%22Four%22%2C%20%22Five%22%2C%20%22Six%22%2C%20%22Seven%22%2C%20%22Eight%22%2C%20%22Nine%22%2C%20%22Ten%22%5D%0AtaskNames%2EforEach%28taskName%20%3D%3E%20%7B%0A%09tagName%20%3D%20tagTitles%5BMath%2Efloor%28Math%2Erandom%28%29%20%2A%20tagTitles%2Elength%29%5D%0A%09new%20Task%28%22Task%20%22%20%2B%20taskName%29%2EaddTag%28tagNamed%28tagName%29%29%0A%7D%29
Demo Setup Script
 

currentTags = flattenedTags tagTitles = ["To Do", "Doing", "Done"] tagTitles.forEach(tagName => { currentTags.byName(tagName) || new Tag(tagName) }) taskNames = ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"] taskNames.forEach(taskName => { tagName = tagTitles[Math.floor(Math.random() * tagTitles.length)] new Task("Task " + taskName).addTag(tagNamed(tagName)) })