Plug-In: Append Copied Spreadsheet ![Plug-in is compatible with Apple Vision Pro Apple Vision Pro]()
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 IDvar shouldLog = truefunction checkLengths(rowData){x = rowData[0].lengthfor (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 TEXTtabbedText = Pasteboard.general.string// DIVIDE INTO ARRAYS OF ROW DATAcopiedData = tabbedText.split('\n').map(line => line.split('\t'))if(shouldLog){console.log("copiedData", copiedData)}// CHECK LENGTH OF EVERY ROWcheckResult = checkLengths(copiedData)if(!checkResult){throw {name: "Data Integrity Issue",message: "Not all rows have the same number of columns."}}// PREFERENCEStabStopIndex = 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 FORMnumTabStopOptions = [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 SETTINGScolumnCount = 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 PREFERENCESpreferences.write("tabStopIndex", columnCount)preferences.write("headerTabTypeIndex", headerTabTypeIndex)preferences.write("dataTabTypeIndex", dataTabTypeIndex)preferences.write("tabStopWidthIndex", tabStopWidthIndex)preferences.write("firstTabStopIndex", firstTabStopIndex)// CONVERT TO NUMERIC VALUEStabStopWidthValue = parseInt(tabStopWidth)firstTabOffsetValue = parseInt(firstTabOffset)// EXTRACT AND REMOVE THE FIRST DATA ROWcolumnHeaders = copiedData.shift()// TAB STOPS FOR COLUMN HEADERS ROWcolumnHeaderTabStops = String(firstTabOffsetValue) + headerTabTypeIndicatorfor (i = 1; i < columnCount; i++) {columnHeaderTabStops += "," + String(firstTabOffsetValue + (i * tabStopWidthValue)) + headerTabTypeIndicator}console.log(columnHeaderTabStops)// TAB STOPS FOR CONTENT ROWScontentTabStops = String(firstTabOffsetValue) + dataTabTypeIndicatorfor (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 OBJECTtableTextObj = new Text("", baseStyle)// CREATE TEXT OBJECT FOR COLUMN HEADERS ROWcolumnHeadersStr = 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 OBJECTtableTextObj.append(txtObj, baseStyle)// CREATE A NEW LINE TEXT OBJECTnewLineObject = new Text("\n", baseStyle)// APPEND DATA ROWS TO PARENT TEXT OBJECTfor (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 NOTEnoteObj = selectedItem.noteTextnoteObj.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;})();