Plug-In: Append Copied Spreadsheet
If you wish to include formatted spreadsheet data in the note of a project or task, here’s a plug-in that will append the copied spreadsheet data to the note of the selected item using the user-indicated tab formatting.
REQUIREMENTS:
- OmniFocus 4 or newer
- Copied spreadsheet data on the clipboard
- Copied spreadsheet data must include BOTH row and column headers
TIPS:
- Once the spreadsheet data is appended to the note of the selected item, use the Character Attributes and Paragraph Attributes plug-ins to format the note text appropriately.
- On macOS, add text manipulation plug-ins to the OmniFocus Toolbar for quick access.
- DOWNLOAD example Numbers spreadsheet
Return to: OmniFocus Plug-In Collection
Append Copied Spreadsheet
/*{
"type": "action",
"targets": ["omnifocus"],
"author": "Otto Automator",
"identifier": "com.omni-automation-of.append-copied-spreadsheet",
"version": "1.0",
"description": "Creates a table using the copied spreadsheet data and appends it to the note of the selected project or task. NOTE: Both row and column headers are required to be included within the copied data.",
"label": "Append Copied Spreadsheet",
"shortLabel": "Append Spreadsheet",
"paletteLabel": "Append Spreadsheet",
"image": "tablecells"
}*/
(() => {
var preferences = new Preferences() // NO ID = PLUG-IN ID
var shouldLog = true
function checkLengths(rowData){
x = rowData[0].length
for (i = 1; i < rowData.length; i++) {
if(rowData[i].length !== x){return false}
}
return true
}
const action = new PlugIn.Action(async function(selection, sender){
try {
selectedItem = selection.databaseObjects[0]
// RETRIEVE CLIPBOARD TEXT
tabbedText = Pasteboard.general.string
// DIVIDE INTO ARRAYS OF ROW DATA
copiedData = tabbedText.split('\n').map(line => line.split('\t'))
if(shouldLog){console.log("copiedData", copiedData)}
// CHECK LENGTH OF EVERY ROW
checkResult = checkLengths(copiedData)
if(!checkResult){
throw {
name: "Data Integrity Issue",
message: "Not all rows have the same number of columns."
}
}
// PREFERENCES
tabStopIndex = preferences.readNumber("tabStopIndex")
if(!tabStopIndex){
preferences.write("tabStopIndex", 5)
tabStopIndex = 5
}
headerTabTypeIndex = preferences.readNumber("headerTabTypeIndex")
if(!headerTabTypeIndex){
preferences.write("headerTabTypeIndex", 1)
headerTabTypeIndex = 1
}
dataTabTypeIndex = preferences.readNumber("dataTabTypeIndex")
if(!dataTabTypeIndex){
preferences.write("dataTabTypeIndex", 2)
dataTabTypeIndex = 2
}
tabStopWidthIndex = preferences.readNumber("tabStopWidthIndex")
if(!tabStopWidthIndex){
preferences.write("tabStopWidthIndex", 5)
tabStopWidthIndex = 5
}
firstTabStopIndex = preferences.readNumber("firstTabStopIndex")
if(!firstTabStopIndex){
preferences.write("firstTabStopIndex", 5)
firstTabStopIndex = 5
}
// CREATE AND PRESENT FORM
numTabStopOptions = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
numTabStopNames = ["3", "4", "5", "6", "7", "8", "9", "10", "11", "12"]
numTabStopsMenu = new Form.Field.Option(
"tabStopCount",
"Number of Data Columns",
numTabStopOptions,
numTabStopNames,
tabStopIndex
)
tabTypesOptions = [0, 1, 2, 3]
tabTypesNames = ["Left-Aligned", "Center-Aligned", "Right-Aligned", "Decimal-Aligned"]
tabTypesIndicators = ["L", "C", "R", "D"]
headerTabTypesMenu = new Form.Field.Option(
"headerTabType",
"Column Headers Tab Type",
tabTypesOptions,
tabTypesNames,
headerTabTypeIndex
)
dataTabTypesMenu = new Form.Field.Option(
"dataTabType",
"Data Columns Tab Type",
tabTypesOptions,
tabTypesNames,
dataTabTypeIndex
)
tabStopWidthsOptions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
tabStopWidthsNames = ["28", "36", "48", "56", "64", "72", "84", "96", "112", "128", "140", "168", "196", "212", "224"]
tabStopWidthsMenu = new Form.Field.Option(
"tabStopWidth",
"Distance Between Tabs (points)",
tabStopWidthsOptions,
tabStopWidthsNames,
tabStopWidthIndex
)
firstTabStopOffsetOptions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
firstTabStopOffsetNames = ["0", "16", "32", "48", "64", "72", "96", "112", "128", "144", "160", "176", "192", "208", "224", "240"]
firstTabStopOffsetMenu = new Form.Field.Option(
"firstTabOffset",
"Width of Row Headers Column (points)",
firstTabStopOffsetOptions,
firstTabStopOffsetNames,
firstTabStopIndex
)
inputForm = new Form()
inputForm.addField(headerTabTypesMenu)
inputForm.addField(dataTabTypesMenu)
inputForm.addField(numTabStopsMenu)
inputForm.addField(tabStopWidthsMenu)
inputForm.addField(firstTabStopOffsetMenu)
formPrompt = "Formatting for copied spreadsheet:"
buttonTitle = "Continue"
formObject = await inputForm.show(formPrompt,buttonTitle)
// RETRIEVE USER-PROVIDED SETTINGS
columnCount = formObject.values["tabStopCount"]
if(shouldLog){console.log("columnCount", columnCount)}
headerTabTypeIndex = formObject.values["headerTabType"]
headerTabTypeIndicator = tabTypesIndicators[headerTabTypeIndex]
if(shouldLog){console.log("headerTabTypeIndicator", headerTabTypeIndicator)}
dataTabTypeIndex = formObject.values["dataTabType"]
dataTabTypeIndicator = tabTypesIndicators[dataTabTypeIndex]
if(shouldLog){console.log("dataTabTypeIndicator", dataTabTypeIndicator)}
tabStopWidthIndex = formObject.values["tabStopWidth"]
tabStopWidth = tabStopWidthsNames[tabStopWidthIndex]
if(shouldLog){console.log("tabStopWidth", tabStopWidth)}
firstTabStopIndex = formObject.values["firstTabOffset"]
firstTabOffset = firstTabStopOffsetNames[firstTabStopIndex]
if(shouldLog){console.log("firstTabOffset", firstTabOffset)}
// WRITE VALUES TO PREFERENCES
preferences.write("tabStopIndex", columnCount)
preferences.write("headerTabTypeIndex", headerTabTypeIndex)
preferences.write("dataTabTypeIndex", dataTabTypeIndex)
preferences.write("tabStopWidthIndex", tabStopWidthIndex)
preferences.write("firstTabStopIndex", firstTabStopIndex)
// CONVERT TO NUMERIC VALUES
tabStopWidthValue = parseInt(tabStopWidth)
firstTabOffsetValue = parseInt(firstTabOffset)
// EXTRACT AND REMOVE THE FIRST DATA ROW
columnHeaders = copiedData.shift()
// TAB STOPS FOR COLUMN HEADERS ROW
columnHeaderTabStops = String(firstTabOffsetValue) + headerTabTypeIndicator
for (i = 1; i < columnCount; i++) {
columnHeaderTabStops += "," + String(firstTabOffsetValue + (i * tabStopWidthValue)) + headerTabTypeIndicator
}
console.log(columnHeaderTabStops)
// TAB STOPS FOR CONTENT ROWS
contentTabStops = String(firstTabOffsetValue) + dataTabTypeIndicator
for (i = 1; i < columnCount; i++) {
contentTabStops += "," + String(firstTabOffsetValue + (i * tabStopWidthValue)) + dataTabTypeIndicator
}
console.log(contentTabStops)
console.log(JSON.stringify(columnHeaders))
console.log(JSON.stringify(copiedData))
// CREATE PARENT TEXT OBJECT
tableTextObj = new Text("", baseStyle)
// CREATE TEXT OBJECT FOR COLUMN HEADERS ROW
columnHeadersStr = columnHeaders.join('\t')
txtObj = new Text(columnHeadersStr, baseStyle)
txtObj.style.set(Style.Attribute.ParagraphTabStops, columnHeaderTabStops)
txtObj.style.set(Style.Attribute.ParagraphDefaultTabInterval, tabStopWidthValue)
// ADD COLUMN HEADERS ROW TO PARENT TEXT OBJECT
tableTextObj.append(txtObj, baseStyle)
// CREATE A NEW LINE TEXT OBJECT
newLineObject = new Text("\n", baseStyle)
// APPEND DATA ROWS TO PARENT TEXT OBJECT
for (indx in copiedData){
aRow = copiedData[indx]
aRowStr = aRow.join('\t')
txtObj = new Text(aRowStr, baseStyle)
txtObj.style.set(Style.Attribute.ParagraphTabStops, contentTabStops)
txtObj.style.set(Style.Attribute.ParagraphDefaultTabInterval, tabStopWidthValue)
tableTextObj.append(newLineObject, baseStyle)
tableTextObj.append(txtObj, baseStyle)
}
// APPEND PARENT OBJECT TO ITEM NOTE
noteObj = selectedItem.noteText
noteObj.append(tableTextObj, baseStyle)
}
catch(err){
if(!err.causedByUserCancelling){
console.error(err.name, err.message)
new Alert(err.name, err.message).show()
}
}
});
action.validate = function(selection, sender){
singleItemIsSelected = (
selection.databaseObjects.length === 1 &&
selection.projects.length === 1 ||
selection.tasks.length === 1
)
return (Pasteboard.general.hasStrings && singleItemIsSelected)
};
return action;
})();