Plug-In: Replicate Task in Hive

One the primary strengths of Omni Automation is its ability to integrate with 3rd-party applications and systems through the use of standard communication APIs. Hive is an online project management platform for groups and teams. The following plug-in for OmniFocus enables a selected task to be replicated into your Hive account.

Video: Replicate Task in Hive
Create a replication of the selected OmniFocus task in Hive.

This plug-in uses the Omni Automation Credentials and Preferences classes to store the relevant Hive account information required to use the Hive API to create actions. Using these classes, the Hive User ID and API Key are stored securely in the system Keychain, and your Hive Workspace ID is stored in the plug-in’s preference file. All stored data items are accessed automatically by the plug-in during subsequent executions of the plug-in.

To reset stored data, hold down the Control key when selecting the plug-in from the OmniFocus Automation Menu, and approve the forthcoming dialogs.

The Omni Automation URL.FetchRequest and URL.FetchResponse classes are used to communicate with the Hive API servers to edit the elements in the targeted Hive account and workspace.

All of these classes are implemented in such a way as to minimize the need for the user to interact with them. The user of the plug-in simply selects the OmniFocus task to replicate and then runs the plug-in.

Initial Plug-In Setup

The first time the plug-in is run, or is run after clearing stored data, the following dialogs will be presented. Be sure to have your Hive User ID, API Key, and Workspace ID available when setting up the plug-in.

The first dialog is for entering and storing your Hive User ID and API Key:

Hive User ID API ID dialog

The second dialog is for entering and storing your Hive Workspace ID:

Hive workspace ID dialog

As mentioned above, stored Hive data can be changed or removed by holding down the Control key when selecting the plug-in from the OmniFocus Automation Menu.

Running the Plug-In

After setting up the plug-in by storing Hive account data, subsequent runs of the plug-in will present the following dialog containing two checkboxes for indicating whether the plug-in should insert links to the task and/or action into the corresponding task note or action description.

Hive links options dialog

The two checkbox settings are also stored in the plug-in preferences file and will be automatically retrieved and used.

Task Data Used to Create Actions

The following data of the selected task is used to replicate the task as a Hive action:

Success alert

DISCLAIMER: Mention of third-party websites and products is for informational purposes only and constitutes neither an endorsement nor a recommendation. OMNI-AUTOMATION.COM assumes no responsibility with regard to the selection, performance or use of information or products found at third-party websites. OMNI-AUTOMATION.COM provides this only as a convenience to our users. OMNI-AUTOMATION.COM has not tested the information found on these sites and makes no representations regarding its accuracy or reliability. There are risks inherent in the use of any information or products found on the Internet, and OMNI-AUTOMATION.COM assumes no responsibility in this regard. Please understand that a third-party site is independent from OMNI-AUTOMATION.COM and that OMNI-AUTOMATION.COM has no control over the content on that website. Please contact the vendor for additional information.

Return to: OmniFocus Plug-In Collection

Replicate Task in Hive

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of.replicate-task-in-hive", "version": "1.0", "description": "Replicates the selected task in the provided Hive workspace, whose ID is automatically stored in the plug-in preferences. To change the workspace ID hold down the Control key when selecting plug-in from Automation menu.", "label": "Replicate Task in Hive", "shortLabel": "Task to Hive", "paletteLabel": "Task to Hive", "image": "rectangle.and.paperclip" }*/ (() => { var shouldLogToConsole = true var serviceTitle = "Hive" var credentials = new Credentials() var preferences = new Preferences() var action = new PlugIn.Action(async function(selection, sender){ var credentialsObj = credentials.read(serviceTitle) var workspaceID = preferences.readString("workspaceID") if (app.controlKeyDown){ // PROMPT FOR CREDENTIALS alertMessage = "Remove the stored credentials?" alert = new Alert("Confirmation Required", alertMessage) alert.addOption("Reset") alert.addOption("Cancel") buttonIndex = await alert.show() if (buttonIndex === 0){ credentials.remove(serviceTitle) } // PROMPT FOR WORKSPACE ID alertMessage = "Remove the stored Workspace ID?" alert = new Alert("Confirmation Required", alertMessage) alert.addOption("Remove") alert.addOption("Skip") buttonIndex = await alert.show() if (buttonIndex === 0){ preferences.remove("workspaceID") } } else if (!credentialsObj){ inputForm = new Form() userIDField = new Form.Field.String( "userID", "User ID", null ) if(Device.ipad || Device.ipad){ userIDField.autocapitalizationType = TextAutocapitalizationType.None } APIKeyField = new Form.Field.String( "apiKey", "API Key", null ) if(Device.ipad || Device.ipad){ APIKeyField.autocapitalizationType = TextAutocapitalizationType.None } inputForm.addField(userIDField) inputForm.addField(APIKeyField) inputForm.validate = formObject => { userID = formObject.values["userID"] apiKey = formObject.values["apiKey"] validation = (userID && apiKey) ? true:false return validation } formPrompt = "Enter Hive User ID and API key:" formObject = await inputForm.show(formPrompt, "Continue") userID = formObject.values["userID"] apiKey = formObject.values["apiKey"] credentials.write(serviceTitle, userID, apiKey) } else if (!workspaceID){ defaultWorkspaceID = "" workspaceID = preferences.readString("workspaceID") if(!workspaceID){ workspaceID = defaultWorkspaceID } textInputField = new Form.Field.String( "workspaceID", "Workspace ID", workspaceID ) inputForm = new Form() inputForm.addField(textInputField) inputForm.validate = function(formObject){ providedValue = formObject.values["workspaceID"] return (providedValue && providedValue.length > 0) ? true:false } formPrompt = "Enter Hive Workspace ID:" buttonTitle = "Continue" formObject = await inputForm.show(formPrompt, buttonTitle) workspaceID = formObject.values["workspaceID"] preferences.write("workspaceID", workspaceID) } else { try { var userID = credentialsObj["user"] var apiKey = credentialsObj["password"] var workspaceID = preferences.readString("workspaceID") if(shouldLogToConsole){ console.log("USER-ID:", userID) console.log("API-KEY:", apiKey) console.log("WORKSPACE ID:", workspaceID) } linkToAction = preferences.readBoolean("linkToAction") if(!linkToAction){ linkToAction = false } linkToActionField = new Form.Field.Checkbox( "linkToAction", "Append link to new action to task note.", linkToAction ) linkToTask = preferences.readBoolean("linkToTask") if(!linkToTask){ linkToTask = false } linkToTaskField = new Form.Field.Checkbox( "linkToTask", "Append link to task to action description.", linkToAction ) inputForm = new Form() inputForm.addField(linkToActionField) inputForm.addField(linkToTaskField) formPrompt = action.label formObject = await inputForm.show(formPrompt, "Continue") var linkToAction = formObject.values["linkToAction"] preferences.write("linkToAction", linkToAction) var linkToTask = formObject.values["linkToTask"] preferences.write("linkToTask", linkToTask) if(shouldLogToConsole){ console.log("ADD ACTION LINK:", linkToAction) console.log("ADD TASK LINK:", linkToTask) } // GATHER SELECETED TASK DATA var task = selection.tasks[0] var taskTitle = task.name taskNote = task.note if(!taskNote){taskNote = ""} taskLink = "omnifocus:///task/" + task.id.primaryKey markdownLink = `[TASK LINK](${taskLink})` HTMLBackLink = `<a href="${taskLink}">LINK</a>` if(taskNote.length === 0){ if(linkToTask){ actionDescription = HTMLBackLink } else { actionDescription = "" } } else { if(linkToTask){ actionDescription = taskNote + " \n" + HTMLBackLink } else { actionDescription = taskNote } } if(shouldLogToConsole){ console.log("DESCRIPTION:", actionDescription) } taskDue = task.dueDate if(taskDue){ var scheduledString = taskDue.toISOString() } else { var scheduledString = new Date().toISOString() } if(shouldLogToConsole){ console.log("SCHEDULED:", scheduledString) } // CONSTRUCT ELEMENTS FOR POST baseUrl = "https://app.hive.com/api/v1/" endpoint = "actions/create" url = baseUrl + endpoint urlComponents = URL.Components.fromString(url) q1 = new URL.QueryItem("user_id", userID) q2 = new URL.QueryItem("api_key", apiKey) urlComponents.queryItems = [q2, q1] var urlStr = urlComponents.url.string if(shouldLogToConsole){console.log("URL:", urlStr)} data = JSON.stringify({ workspace: `${workspaceID}`, title: taskTitle, assigned_to: `${userID}`, scheduledDate: `${scheduledString}`, description: `${actionDescription}` }) if(shouldLogToConsole){console.log("DATA:", data)} request = URL.FetchRequest.fromString(url) request.bodyString = data request.method = 'POST' request.headers = { Accept: "application/json", "Content-Type": "application/json" } request.url = URL.fromString(urlStr) var response = await request.fetch() responseCode = response.statusCode if(shouldLogToConsole){console.log("CODE:", responseCode)} if (responseCode >= 200 && responseCode < 300){ console.log(JSON.stringify(response.headers)) console.log(JSON.stringify(response.bodyString)) responseData = JSON.parse(response.bodyString) actionID = responseData.id actionWSPID = responseData.workspace if(linkToAction){ actionLink = `https://app.hive.com/workspace/${actionWSPID}?actionId=${actionID}` if (task.note){ task.appendStringToNote("\nHive Action: " + actionLink) } else { task.appendStringToNote("Hive Action: " + actionLink) } var alertMessage = "A link has been appended to the note of the selected task." } else { var alertMessage = "A replicate of the task has been added to Hive." } new Alert("Hive Action Created", alertMessage).show() } else { errorObj = JSON.parse(response.bodyString) throw {"name": errorObj.error, "message": errorObj.message} } } catch(err){ if(!err.message.includes("cancelled")){ new Alert(err.name, err.message).show() } } } }); action.validate = function(selection, sender){ return (selection.tasks.length === 1) }; return action; })();