Does your organization maintain a group calendar of staff events, trainings, room rentals, or any other set of dates that are also stored somewhere in Salesforce? Do you wish there were a way to display them on a calendar?
One option would be to use workflow to create an event on every user’s calendar whenever an object is saved. But that creates noise for users, and isn’t nearly as intuitive as having a separate calendar for a given purpose. What you really want is a group calendar available to everyone that displays only events pertaining to one thing.
The use case
My client, a theater that sells blocks of tickets to its shows using Salesforce, needed a place to track the status of all their bookings on a calendar. To meet their requirement, I created a public calendar in their account. (See my general post on group/public calendars on the NPower Seattle blog for how to do this.)

Getting the events on the calendar
My first thought was to use workflow to put events on that new calendar whenever an opp was moved to the right stage. It turns out you can’t do that – although a public calendar can be the “owner” of an event, you can’t select a public calendar as the user for a workflow task or event. Besides, the client needed not only to put this event on the calendar in the first place, but to update it automatically anytime the stage of the opportunity changes.
To maintain the events on the calendar, I created the following trigger:
trigger oppChangeAfter on Opportunity (after delete, after insert, after undelete, after update) {
// trigger to manage events on the shared theater Show Calendar
// written by Evan Callahan, copyright (c) 2009 NPower Seattle
// released under the GNU General Public License, http://www.gnu.org/licenses/gpl.html
// need to know the salesforce ID of our public calendar
final id publicCalendarId = '02380000000YHci';
// this flag allows us to bypass all queries if the opp is not the type that goes on the calendar
boolean createEvents = false;
// get acct names to put them in the events we create
set oppAccts = new set();
map acctMap;
if (!trigger.isDelete) {
set acctIds = new set();
for (opportunity o : trigger.new) {
if (o.stageName != 'Quote' && o.stageName != 'Closed Lost' && o.show_date__c != null
&& o.show_date__c >= system.today().addMonths(-1) && o.theater_city__c != null) {
createEvents = true;
oppAccts.add(o.accountId);
}
}
if (createEvents) acctMap = new map([select id, name from account where id in : oppAccts]);
}
// get opp ids in this trigger set
set oppIds = (trigger.isDelete ? trigger.oldmap : trigger.newmap).keyset();
// get contacts for these opps so we can attach them to the events
map oppCons = new map();
if (createEvents) {
for (opportunityContactRole ocr : [select opportunityid, contactid
from opportunityContactRole
where opportunityId in : oppIds and isPrimary = true]) {
oppCons.put(ocr.opportunityId, ocr.contactId);
}
}
// get existing events for these opps - need to update them
map tzShowEventMap = new map();
for (event e : [Select id, e.WhoId, e.WhatId, e.Subject, e.OwnerId, e.IsReminderSet, e.IsAllDayEvent, e.ActivityDate
From Event e where whatId in : oppIds and ownerId =: publicCalendarId ]) {
tzShowEventMap.put(e.whatId, e);
}
event[] eventsToDelete = new event[0];
if (trigger.isDelete) {
eventsToDelete = tzShowEventMap.values();
} else if (createEvents) {
// add an event for the show
for (opportunity o : trigger.new) {
if (o.stageName != 'Quote' && o.stageName != 'Closed Lost' && o.show_date__c != null
&& o.show_date__c >= system.today().addMonths(-1) && o.theater_city__c != null) {
// create the event or get the existing one
event e = tzShowEventMap.containsKey(o.id) ? tzShowEventMap.get(o.id) : new Event();
e.isReminderSet = false;
e.isAllDayEvent = true;
// connect to the contact, if any
e.whoId = oppCons.containsKey(o.id) ? oppCons.get(o.id) : null;
// connect to the opp
e.whatId = o.id;
// get the acct name
string acctName = (o.accountId==null || !acctMap.containsKey(o.accountId)) ? '[Unknown]' : acctMap.get(o.accountid).name;
if (acctName.length() > 50) acctName = acctName.substring(0, 50);
// the subject of the event includes the opp stage, acct name, and number of tickets purchased
// example - 45 Booked: NPower Seattle
e.Subject = ((o.total_tickets__c != null && o.total_tickets__c > 0) ? o.total_tickets__c.format() + ' ': '') +
(o.StageName == 'Closed Won' ? 'Closed' : o.StageName) + ': ' + acctName;
// the date of the show is in a custom field
e.activityDate = o.show_date__c;
// to put it on the right calendar, set the owner ID to the calendar ID
e.ownerId = publicCalendarId;
// put this event on the map
tzShowEventMap.put(o.id, e);
} else {
// delete events with opps that no longer fit the bill
if (tzShowEventMap.containsKey(o.id)) eventsToDelete.add(tzShowEventMap.get(o.id));
}
}
// add and update!
upsert tzShowEventMap.values();
}
if (!eventsToDelete.isEmpty()) delete eventsToDelete;
}
Another option for a truly public calendar
This calendar is working well – we created a link to it for all users, and they can hover over events on the calendar to see the details or link directly to the opportunity or associated contact. However, the calendar is not publicly available outside Salesforce.
Another option we considered was using the Google Apps API to add the opportunities to an external Google calendar. That would allow us to share the calendar with employees or partners who don’t have Salesforce accounts – or even to the general public. After taking a peek at the API on the developer site, I don’t think it would be much more effort. The Google calendar wouldn’t offer direct links to the Salesforce objects, but it would be a lot more flexible than the calendar in Salesforce.