×

OmniPlan: Conference Example (Fetch Remote Data)

In the following version of the Conference Example plug-in, the JSON data used to construct the conference outline is retrieved from a password-protected file stored on this website.

The plug-in code incorporates four instances of await statements within an asynchronous wrapper rather than relying on explicit Promise handlers and code blocks.

The plug-in performs the following steps:

Conference Example (Fetch Remote Data)
 

/*{ "type": "action", "targets": ["omniplan"], "author": "Otto Automator", "identifier": "com.omni-automation.op.fetch-build-conference-example", "version": "2.5", "description": "Adapts the current project to display a basic 1-track conference. Conference data is downloaded from a password-protected JSON file. The password is: omni-automation", "label": "Fetch and Build Single-Track Conference", "shortLabel": "Conference", "paletteLabel": "Conference", "image": "calendar" }*/ (() => { const action = new PlugIn.Action(async function(selection, sender){ try { // INFORMATIONAL ALERT let openAlertTitle = "Conference Example" let openAlertMessage = "This plug-in will create a scenario for a 3-day single-track conference using data downloaded from a password-protected JSON file.\n\nThe password is: omni-automation" let openAlertResult = await new Alert(openAlertTitle, openAlertMessage).show() // CREATE FORM FOR GATHERING USER INPUT let inputForm = new Form() // CREATE TEXT FIELD AND DATE FIELD OBJECTS let conferenceTitleField = new Form.Field.String( "conferenceTitle", "Conference Title", null ) let conferenceLengthMenu = new Form.Field.Option( "conferenceLength", "Length (in days)", [1,2,3], ["1", "2", "3"], 3 ) // GET 2ND MONDAY OF NEXT MONTH let cal = Calendar.current let today = cal.startOfDay(new Date()) let dc = cal.dateComponentsFromDate(today) dc.day = 1 dc.month = dc.month + 1 let monthLength = new Date(dc.year, dc.month, 0).getDate() let matchedDays = new Array() let weekdayIndex = 1 // Monday for (var i = 1; i < (monthLength + 1); i++) { dc.day = i var d = cal.dateFromDateComponents(dc) if (d.getDay() === weekdayIndex){matchedDays.push(d)} } let targetMonday = matchedDays[1] let startDateField = new Form.Field.Date( "startDate", "Start Date (Monday)", targetMonday ) // ADD THE FIELDS TO THE FORM inputForm.addField(conferenceTitleField) inputForm.addField(conferenceLengthMenu) inputForm.addField(startDateField) // VALIDATE THE USER INPUT. DAY MUST BE A MONDAY, AND TITLE MUST BE PROVIDED. inputForm.validate = function(formObject){ let textValue = formObject.values["conferenceTitle"] let textStatus = (textValue && textValue.length > 0) ? true:false let startDateObject = formObject.values["startDate"] let startDateStatus = (startDateObject && startDateObject.getDay() === 1) ? true:false let validation = (textStatus && startDateStatus) ? true:false return validation } // PRESENT THE FORM TO THE USER let formPrompt = "Provide the conference title, length in days, and the conference start date (Monday):" let formObject = await inputForm.show(formPrompt,"Continue") // TITLE let conferenceTitle = formObject.values['conferenceTitle'] document.project.title = conferenceTitle // START DATE. SET TIME OF THE CHOSEN DATE TO MIDNIGHT, AND APPLY DATE AS THE PROJECT’S START DATE. let startDate = formObject.values['startDate'] startDate = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate(), 0, 0, 0) actual.startDate = startDate // CONFERENCE LENGTH IN DAYS let conferenceLength = formObject.values['conferenceLength'] conferenceLength = parseInt(conferenceLength) // CLEAR ALL TASKS AND RESOURCES actual.rootTask.descendents().forEach((task)=>{task.remove()}) actual.rootResource.descendents().forEach((rsc)=>{rsc.remove()}) // SESSION PARAMETERS let sessionTimes = ["8:30","9:45","11:00","13:30","14:45","16:00"] let sessionDuration = Duration.elapsedHourMinSec(1, 0, 0) // CREATE THE SESSIONS (TASKS) USING THE DAY/SESSION INDEXES TO DERIVE SESSION IDS (101, 203, 305, ETC.) let dayIndex for (dayIndex = 1; dayIndex < conferenceLength + 1; dayIndex++) { let aDate = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate() + (dayIndex- 1) , 0, 0, 0) sessionTimes.forEach((time, sessionIndex) => { let hoursMinutes = time.split(":") let hours = Number(hoursMinutes[0]) let minutes = Number(hoursMinutes[1]) let taskDateTime = new Date(aDate.getFullYear(), aDate.getMonth(), aDate.getDate(), hours, minutes, 0) let task = actual.rootTask.addSubtask() // check for 4.x property, otherwise use 3.x property if (task.manualStartDate !== undefined){ task.manualStartDate = taskDateTime } else { task.lockedStartDate = taskDateTime } task.duration = sessionDuration task.title = String(dayIndex) + "0" + String(sessionIndex) }) } // CREATE FORM FOR RETRIEVING SECURE REMOTE DATA let fetchForm = new Form() // CREATE TEXT FIELDS let urlField = new Form.Field.String( "targetURL", "URL", "https://omni-automation.com/secure/conference-sessions.json" ) let nameField = new Form.Field.String( "accountName", "Name", "otto-automator" ) let passwordField = new Form.Field.Password( "accountPassword", "Password", null ) // ADD THE FIELDS TO THE FORM fetchForm.addField(urlField) fetchForm.addField(nameField) fetchForm.addField(passwordField) // VALIDATE THE USER INPUT fetchForm.validate = function(formObject){ let targetURL = formObject.values["targetURL"] let schema = ["http://","https://"] let urlStatus = false if (targetURL){ let i; for (i = 0; i < schema.length; i++) { if (targetURL.startsWith(schema[i], 0) && URL.fromString(targetURL)){urlStatus = true} } } let providedName = formObject.values["accountName"] let nameStatus = (providedName && providedName.length > 0) ? true:false let providedPassword = formObject.values["accountPassword"] let passwordStatus = (providedPassword && providedPassword.length > 0) ? true:false let validation = (urlStatus && nameStatus && passwordStatus) ? true:false return validation } // PRESENT THE FORM TO THE USER let fetchFormPrompt = "Retrieve Secure Conference Data" let fetchFormObject = await fetchForm.show(fetchFormPrompt, "Continue") // CONSTRUCT ACCOUNT/PASSWORD CREDENTIALS let accountName = fetchFormObject.values["accountName"] let accountPassword = fetchFormObject.values["accountPassword"] let data = Data.fromString(accountName + ":" + accountPassword) let credentials = data.toBase64() // CONSTRUCT HTTP REQUEST let targetURL = fetchFormObject.values["targetURL"] let request = URL.FetchRequest.fromString(targetURL) request.method = 'GET' request.cache = "no-cache" request.headers = {"Authorization": "Basic" + " " + credentials} // EXECUTE REQUEST let response = await request.fetch() let responseCode = response.statusCode if (responseCode >= 200 && responseCode < 300){ let json = JSON.parse(response.bodyString) json.forEach(sessionData => { // EXTRACT DATA FOR EACH SESSION let speakerName = sessionData["Speaker"] let speakerEmail = sessionData["Email"] let sessionID = sessionData["ID"] let sessionTitle = sessionData["Title"] // PROCESS EACH EXISTING TASK let tasks = actual.rootTask.subtasks tasks.forEach(task => { if (task.title === sessionID){ task.title = sessionID + ": " + sessionTitle let rsc = actual.resourceNamed(speakerName) if(!rsc){ rsc = actual.rootResource.addMember() rsc.type = ResourceType.staff rsc.name = speakerName rsc.email = speakerEmail rsc.costPerUse = Decimal.fromString("500.00") } task.addAssignment(rsc) } }) }) // SAVE THE DOCUMENT document.project.title = conferenceTitle document.save() } else { new Alert(String(responseCode), "An HTTP Request error occurred.").show() console.error(JSON.stringify(response.headers)) } } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } }); action.validate = function(selection, sender){ return true }; return action; })();