Last month we looked at integrating DayBack with the Volunteers app for Salesforce. This month we’ll look at integrating DayBack with Taskfeed, the highly rated project management app for Salesforce. Taskfeed does a great job of showing the priorities and dependencies of your tasks, and some customers want to also see a grid-like view of who’s assigned to those tasks and how those tasks may overlap with other Salesforce events or Google Calendar events.
Even though Taskfeed is a managed package, we still have plenty of ways to create a pretty seamless integration between it and DayBack.
Big thanks to Andy Mahood of Taskfeed for his sweet app and for suggesting some of these modifications.
Overview (movie)
Here’s an overview of what the integration lets you accomplish:
The rest of this article describes how this was done. Please use these tips to create your own integration with Taskfeed, or as inspiration for connecting DayBack to other workflows.
Mapping the Taskfeed Objects
Taskfeed has several custom objects associated with it, but the ones we’re interested in for scheduling purposes are boards and tasks. In database terms, boards are the parent of tasks. Taskfeed provides its own task object, and that’s the object we’ll be referring to here, not the native Salesforce task object. DayBack can map to any object with a date (or datetime) field, so let’s look at how we can map these Taskfeed objects.
Boards
Mapping the board object as a DayBack source is straightforward. We have a specific startdate, end date and a name. And we can use DayBack’s standard resource mapping to the board’s owner. Taskfeed tasks can be related to accounts and opportunities and that works perfectly with the two relationships DayBack provides. (For more on how one maps fields in DayBack to the fields in your custom objects, see this article in DayBack’s docs.)
Tasks
However, when we turn our attention to tasks we run into some challenges. The first challenge is mapping to the Taskfeed owner and list fields. (A list in Taskfeed is like the task’s status: “not started” is a list, for example, as is “completed”.) Taskfeed uses relationships for the possible list and owner values, making good use of Salesforce’s relational database capabilities, but making it more difficult for us to map these critical values to DayBack. We could use the two relationship fields provided by DayBack (the contact and project objects in DayBack’s field mapping), but what we really want to do is map owner to DayBack’s resource field and list to DayBack’s status field. This way, we can use DayBack’s resource views to quickly see the task load on a particular user in the org. By mapping list to DayBack’s status field, we can color code by this value, giving a quick visual overview of how the tasks are progressing on a particular board.
The problem is that, in DayBack, status and resource are designed to map to local fields (as opposed to related values), so how can we make this work?
Very much like we did with the Volunteers app, we need to add some proxy fields to the task custom object that DayBack can map too, we can then use Salesforce workflow rules to keep these in sync with the native Taskfeed fields. The fields we’ll add to taskfeed1__Task__c are DBk_OwnerName__c and DBk_ListName__c. Both fields are text with no default values. We can then create a workflow rule to populate these values when the native Taskfeed relationships are updated. Even though these objects are part of a managed package, we can still add fields and workflow rules to them =)
For DBk_OwnerName__c our workflow rule criteria is as follows:
ISCHANGED( taskfeed1__Owner__c )
and then we update the field via workflow action with the following formula:
Taskfeed1__Owner__r.FirstName & " " & taskfeed1__Owner__r.LastName
Since the relationship to owner from task is a one-to-one, the formula above is all we need. We can do the same with list, where our criteria is:
ISCHANGED( taskfeed1__List__c )
and the updating formula is:
taskfeed1__List__r.Name
Adding a Trigger
What about going the other way? If we update the value in DayBack, we need to look up the id for the related list or owner by their name. Unfortunately, we can’t do that with a workflow rule. For this, we need to turn to an apex trigger. We’ll tackle the owner field first. Basically, we need our trigger to detect if our custom field DBk_OwnerName__c has been changed, and if so, we’ll do a SOQL query to return the id based on the name.
Of course, exact duplicate user names in the organization can cause problems with this method, and we are looking at adding support for related fields for DayBack’s status and resource fields, but in most cases/orgs it should work fine to lookup by name this way.
After some tinkering this is the trigger we came up with for updating the owner relationship:
trigger UpdateDBkFields on taskfeed1__Task__c (before insert, before update) { String OwnerName; String OwnerId; Boolean updateOwner = false; taskfeed1__Task__c oldTask; for(taskfeed1__Task__c newTask : trigger.new){ if( trigger.isInsert) { if(newTask.DBk_Owner__c != null) { updateOwner = true; } } else { oldTask = trigger.oldMap.get(newTask.Id); if(oldTask.DBk_Owner__c != newTask.DBk_Owner__c) { updateOwner = true; } } if(updateOwner){ OwnerName = newTask.DBk_Owner__c; List<User> users = [SELECT Id FROM User Where Name = :OwnerName]; OwnerId = users.get(0).Id; newTask.taskfeed1__Owner__c = OwnerId; return; } } }
First, we detect if this an insert or update event. If it’s an insert, we only want to take action if the owner has been assigned to our custom field, otherwise we wouldn’t have anything to look up! If it’s an update, then we only want to take action if the relationship has changed. If either of those cases are true, then we perform our SOQL and update the relationship with the result.
We can do the same thing for lists, which we can either do as its own trigger or combine with the above like this:
trigger UpdateDBkFields on taskfeed1__Task__c (before insert, before update) { String OwnerName; String OwnerId; Boolean updateOwner = false; String ListName; String ListId; Boolean updateList = false; String BoardId; taskfeed1__Task__c oldTask; for(taskfeed1__Task__c newTask : trigger.new){ if( trigger.isInsert) { if(newTask.DBk_OwnerName__c != null) { //new item, flag for update if owner has been assigned updateOwner = true; } if(newTask.DBk_ListName__c != null) { //new item, flag for update if List has been assigned updateList = true; } } else { oldTask = trigger.oldMap.get(newTask.Id); if(oldTask.DBk_OwnerName__c != newTask.DBk_OwnerName__c) { //our owner field has changed, flag for update. updateOwner = true; } if(oldTask.DBk_ListName__c != newTask.DBk_ListName__c) { //our list field has changed, flag for update. updateOwner = true; } } if(updateOwner){ //perform SOQL to obtain id and update relationship OwnerName = newTask.DBk_OwnerName__c; List<User> users = [SELECT Id FROM User Where Name = :OwnerName]; OwnerId = users.get(0).Id; newTask.taskfeed1__Owner__c = OwnerId; } if(updateList){ //perform SOQL to obtain id and update relationship ListName = newTask.DBk_ListName__c; BoardId = newTask.taskfeed1__Board__c; List<taskfeed1__List__c> lists = [SELECT Id FROM taskfeed1__List__c Where Name = :ListName AND taskfeed1__Board__c = :BoardId]; ListId = lists.get(0).Id; newTask.taskfeed1__List__c = ListId; } } }
Once this trigger is in place we can effectively map tasks and get the behavior from the task owner and task list that we’re after:
Now from the resource view we can easily see which tasks are not started and reassign those to another owner via drag and drop as shown in the opening video:
Adding Polish with Event Actions
Once we had tasks properly mapped and working we quickly noticed an issue having to do with the relationship between tasks and their board. Although you can specifically set the start and end date of a board, if you update a task belonging to that board, then the board’s dates will update to accommodate the earliest task start date and the latest task end date. That’s pretty cool and presumably, Taskfeed is doing this with a trigger. The problem is, that when you update a task in DayBack, DayBack has no way of knowing that it should also refresh the parent board. You’d see the change on the next refresh, but we’d like to see the board update in real-time in DayBack. How can we do this?
Custom and event actions are some of the most powerful features available in DayBack. With custom actions, we can create buttons in the calendar popover and run custom JavaScript. And with event actions, we can run custom JavaScript after an event action occurs:
In this case, we want to run our action after our task has been saved. We’ll then have our JavaScript query the board’s new start and end date, update the board object on the calendar, and ask DayBack to refresh it. The following JavaScript works great:
//get our full calendar element via JQuery var element = $('.calendar'); //gather all events in the dom var all = element.fullCalendar('clientEvents'); //get the Tasks' Board Id var boardId = event.projectID[0]; var parent; //find the board object for ( var i in all ) { if(all[i].eventID===boardId) { parent = all[i]; break; } } //now query our updated board var requests = [{"Id":"='" + parent.eventID + "'"}]; fbk.findRecords('taskfeed1__Board__c', actionCallback, requests, parent.schedule.id); function actionCallback(data) { //update parent object start and end based on our result var newStart = $.fullCalendar.moment(data[0].taskfeed1__Start_Date__c); //need to pad the end date by 1 to display correctly var newEnd= $.fullCalendar.moment(data[0].taskfeed1__End_Date__c).add(1,'d'); parent.start = newStart; parent.end = newEnd; element.fullCalendar('updateEvent',parent); }
We now get real-time updating of the board when one of its child tasks are updated!
Going Back and Forth
Both DayBack and Taskfeed support URL parameters so you can navigate to specific parts of either app from the other. Adding buttons to navigate like this can really save time and make the entire solution feel more seamless. Examples include
- Jump right to a task from DayBack (as shown in the movie above) by creating a custom action using the function: dbk.navigate(‘[[Id]]’,true)
- Create a button in DayBack to go right to the board for a given task. The custom action url would be https://your_taskfeed_page_url.com/apex/taskfeed?boardId=[[taskfeed1__Board__c]]#[[Id]] and you’d select “Yes” to “Open in new window”.
- Create buttons in Taskfeed to view a task directly in DayBack, or to filter DayBack Calendar to see just the tasks for a single board. Instructions for creating buttons like this are here.
A Great Fit
Although we did have to do a little work, we were able to get a nice, tight integration that feels like DayBack was built as a part of Taskfeed. We can read, create, and edit boards and tasks from any of the DayBack views. And we can take advantage of DayBack’s resource views and color coding to quickly see who is assigned a specific task and on what list that task currently resides, complimenting Taskfeed perfectly.
Leave a Reply