35
Coding Apex Triggers on Chatter Group Members A case study in coding for collaboration Carolyn Grabill Salesforce.com Software Development Engineer @CarolynCodes

Coding Apex Triggers on Chatter Group Members

Embed Size (px)

DESCRIPTION

Lunchforce is an employee networking app built on the Salesforce Platform. Using Apex Triggers on CollaborationGroupMember (new as of Spring '13), it matches people in a Chatter Group who have never met before, so they can meet to have lunch and network. Join us to see how you can combine Apex Triggers, Scheduled Apex, and Junction Objects to analyze Chatter connections, adding intelligence to any Chatter Group.

Citation preview

Page 1: Coding Apex Triggers on Chatter Group Members

Coding Apex Triggers on Chatter Group MembersA case study in coding for collaboration

Carolyn GrabillSalesforce.comSoftware Development Engineer@CarolynCodes

Page 2: Coding Apex Triggers on Chatter Group Members

Safe harborSafe harbor statement under the Private Securities Litigation Reform Act of 1995: This presentation may contain forward-looking statements that involve risks, uncertainties, and assumptions. If any such uncertainties materialize or if any of the assumptions proves incorrect, the results of salesforce.com, inc. could differ materially from the results expressed or implied by the forward-looking statements we make. All statements other than statements of historical fact could be deemed forward-looking, including any projections of product or service availability, subscriber growth, earnings, revenues, or other financial items and any statements regarding strategies or plans of management for future operations, statements of belief, any statements concerning new, planned, or upgraded services or technology developments and customer contracts or use of our services. The risks and uncertainties referred to above include – but are not limited to – risks associated with developing and delivering new functionality for our service, new products and services, our new business model, our past operating losses, possible fluctuations in our operating results and rate of growth, interruptions or delays in our Web hosting, breach of our security measures, the outcome of any litigation, risks associated with completed and any possible mergers and acquisitions, the immature market in which we operate, our relatively limited operating history, our ability to expand, retain, and motivate our employees and manage our growth, new releases of our service and successful customer deployment, our limited history reselling non-salesforce.com products, and utilization and selling to larger enterprise customers. Further information on potential factors that could affect the financial results of salesforce.com, inc. is included in our annual report on Form 10-K for the most recent fiscal year and in our quarterly report on Form 10-Q for the most recent fiscal quarter. These documents and others containing important disclosures are available on the SEC Filings section of the Investor Information section of our Web site. Any unreleased services or features referenced in this or other presentations, press releases or public statements are not currently available and may not be delivered on time or at all. Customers who purchase our services should make the purchase decisions based upon features that are currently available. Salesforce.com, inc. assumes no obligation and does not intend to update these forward-looking statements.

Page 3: Coding Apex Triggers on Chatter Group Members

Lunchforce Technologies

▪ Using custom junction objects▪ Triggering on Chatter Group Members▪ Implementing an interface in Apex

• Schedulable• Comparable

▪ Posting to Chatter from Apex

Page 4: Coding Apex Triggers on Chatter Group Members

Lunchforce brings the powerof Chatter to the real world.

Get the source: http://bit.ly/DF13Lunch

Page 5: Coding Apex Triggers on Chatter Group Members

Lunchforce’s moving parts

User joins Lunchforce Chatter group

Chatter Group Member Trigger fires

Luncher__c record is created for that User

Admin schedules LunchMatch Apex

Scheduled Apex runs matching algorithm,

posts results to Chatter

Users see Chatter posts, meet up for

lunch together

Page 6: Coding Apex Triggers on Chatter Group Members

Lunchforce Technologies

▪ Using custom junction objects▪ Triggering on Chatter Group Members▪ Implementing an interface in Apex

• Schedulable• Comparable

▪ Posting to Chatter from Apex

Page 7: Coding Apex Triggers on Chatter Group Members

Lunchforce’s Custom Objects

Junction Object

Junction Object

Page 8: Coding Apex Triggers on Chatter Group Members

Records created by matching process

Luncher__cTime Picker

Luncher__cLocation Picker

Lunch_Match__cMatch Record LookupLookup

Page 9: Coding Apex Triggers on Chatter Group Members

Lunchforce Technologies

▪ Using custom junction objects▪ Triggering on Chatter Group Members▪ Implementing an interface in Apex

• Schedulable• Comparable

▪ Posting to Chatter from Apex

Page 10: Coding Apex Triggers on Chatter Group Members

Chatter Group Member Trigger

Page 11: Coding Apex Triggers on Chatter Group Members

CollaborationGroupMember fields▪ CollaborationGroupId

• The Id of the Chatter group joined

▪ CollaborationRole• Whether the User is Standard or Admin group member

▪ MemberId• Id of the User joining the group

▪ NotificationFrequency• Picklist – Daily, Weekly, Never, Post

Page 12: Coding Apex Triggers on Chatter Group Members

Demo▪ Example CollaborationGroupMember Trigger

• AddNewLunchers

▪ Trigger in action

Page 13: Coding Apex Triggers on Chatter Group Members

Lunchforce Technologies

▪ Using custom junction objects▪ Triggering on Chatter Group Members▪ Implementing an interface in Apex

• Schedulable• Comparable

▪ Posting to Chatter from Apex

Page 14: Coding Apex Triggers on Chatter Group Members

Lunchforce’s moving parts

User joins Lunchforce Chatter group

Collaboration Group Member Trigger fires

Luncher__c record is created for that User

Admin schedules LunchMatch Apex

Scheduled Apex runs matching algorithm,

posts results to Chatter

Users see Chatter posts, meet up for

lunch together

Page 15: Coding Apex Triggers on Chatter Group Members

Interfaces▪ A template that defines methods, but does not implement them▪ Apex class implements an interface by implementing its methods▪ Schedulable is an interface built in to Apex▪ Example:

global class scheduledLunchMatch implements Schedulable {

global void execute(SchedulableContext ctx) { goMatchGo(); }}

Page 16: Coding Apex Triggers on Chatter Group Members
Page 17: Coding Apex Triggers on Chatter Group Members

Demo▪ Example Schedulable Class

• ScheduledLunchMatch

▪ Scheduling a job in setup

Page 18: Coding Apex Triggers on Chatter Group Members

Lunchforce Technologies

▪ Using custom junction objects▪ Triggering on Chatter Group Members▪ Implementing an interface in Apex

• Schedulable• Comparable

▪ Posting to Chatter from Apex

Page 19: Coding Apex Triggers on Chatter Group Members

Problem: how do you maximize new matches in a group of people, when some of them have already met?

Page 20: Coding Apex Triggers on Chatter Group Members

Solution: sort the lunchers by how many lunches they’ve already had.

Page 21: Coding Apex Triggers on Chatter Group Members

Demo▪ Example Comparable Class

• LuncherWithHistory

▪ Sorting a list of LuncherWithHistory records• LunchMatcher

Page 22: Coding Apex Triggers on Chatter Group Members

Lunchforce Technologies

▪ Using custom junction objects▪ Triggering on Chatter Group Members▪ Implementing an interface in Apex

• Schedulable• Comparable

▪ Posting to Chatter from Apex

Page 23: Coding Apex Triggers on Chatter Group Members

Posting to Chatter from Apex using FeedItem▪ Represents an entry in the feed, such as changes in a record feed,

including text posts, link posts, and content posts.▪ Example:

FeedItem fi = new FeedItem();fi.ParentId = [object id]; //user, account, etc.fi.Body = “Body of the post”;insert fi;

Page 24: Coding Apex Triggers on Chatter Group Members

Demo▪ Example FeedItem creation

• LuncherWithHistory▪ Example weekly lunch matchup▪ Mobile lunch match results

Page 25: Coding Apex Triggers on Chatter Group Members

Go meet someone new.

Page 26: Coding Apex Triggers on Chatter Group Members

Carolyn GrabillSalesforce.com

Software Development Engineer@CarolynCodes

Get the source: http://bit.ly/DF13Lunch

Page 27: Coding Apex Triggers on Chatter Group Members
Page 28: Coding Apex Triggers on Chatter Group Members

Trigger on CollaborationGroupMembertrigger addNewLunchers on CollaborationGroupMember (after insert, after delete) { if (Trigger.isInsert) { // When a user joins the group, create a // Luncher__c record for them LunchChatter.newLuncherOnGroupJoin(Trigger.new); } else if (Trigger.isDelete) { // When a user leaves the group, mark the // Luncher__c record inactive LunchChatter.makeLuncherInactiveOnGroupLeave(Trigger.old); }}

Page 29: Coding Apex Triggers on Chatter Group Members

//create a new luncher when someone joins the lunch chatter group public static void newLuncherOnGroupJoin(List<CollaborationGroupMember> allNewMembers) { //get the members that joined the lunch match chatter group List<CollaborationGroupMember> members = new List<CollaborationGroupMember>(); for (CollaborationGroupMember cgm : allNewMembers) { if (cgm.CollaborationGroupId == LunchChatter.lunchGroupId) { members.add(cgm); } }

if (!members.isEmpty()) { //for each member, figure out if we need to create a new luncher or update an existing one for (CollaborationGroupMember cgm : members) { if (keySet.contains(cgm.MemberId)) { userIdsForLunchersToActivate.add(cgm.MemberId); } else { userIdsForNewLunchers.add(cgm.MemberId); } }

Page 30: Coding Apex Triggers on Chatter Group Members

//create new lunchers and add them to the upsert list if (!userIdsForNewLunchers.isEmpty()) { for (User u : [SELECT Id, Name FROM User WHERE Id IN :userIdsForNewLunchers]) { Luncher__c newLuncher = new Luncher__c( Name = u.Name, Status__c = 'Active', User__c = u.Id); lunchersToUpsert.add(newLuncher); } } //get existing lunchers to update and add them to the upsert list if (!userIdsForLunchersToActivate.isEmpty()) { for (Luncher__c l : [SELECT Id, Status__c, User__c FROM Luncher__c where User__c

IN :userIdsForLunchersToActivate]) { l.Status__c = 'Active'; lunchersToUpsert.add(l); } } //insert new lunchers, if any were created if (!lunchersToUpsert.isEmpty()) { upsert lunchersToUpsert; } } }

Page 31: Coding Apex Triggers on Chatter Group Members

//mark a luncher inactive when someone leaves the lunch chatter group public static void makeLuncherInactiveOnGroupLeave(List<CollaborationGroupMember> allDeleted) { //get the members that are part of the chatter group you actually care about List<Id> delUserIds = new List<Id>(); for (CollaborationGroupMember cgm : allDeleted) { if (cgm.CollaborationGroupId == LunchChatter.lunchGroupId) { delUserIds.add(cgm.MemberId); } } //set status inactive for lunchers with delUserIds if (!delUserIds.isEmpty()) { List<Luncher__c> lunchersToUpdate = new List<Luncher__c>(); for (Luncher__c l : [SELECT Id, Status__c, User__c FROM Luncher__c WHERE User__c

IN :delUserIds]) { l.Status__c = 'Inactive'; lunchersToUpdate.add(l); } if (!lunchersToUpdate.isEmpty()) { update lunchersToUpdate; } } }

Page 32: Coding Apex Triggers on Chatter Group Members

global class scheduledLunchMatch implements Schedulable {

global void execute(SchedulableContext ctx) { goMatchGo(); }

//shortcut for demos, to perform a match on command public void goMatchGo() { datetime lunchTime = datetime.now();

LunchMatcher lm = new LunchMatcher(); boolean matchSucceeded = lm.performScheduledMatch(lunchTime); System.assertEquals(true, matchSucceeded); }

}

Page 33: Coding Apex Triggers on Chatter Group Members

public boolean performScheduledMatch(DateTime lunchDate) { //get all the active lunchers List<Luncher__c> activeLunchers = [SELECT Id, Name from Luncher__c WHERE Status__c = 'Active']; Map<Id, Luncher__c> luncherMap = new Map<Id, Luncher__c>(); //get all the inactive lunchers List<Luncher__c> inactiveLunchers = [SELECT Id FROM Luncher__c WHERE Status__c = 'Inactive']; //get all the previous matches List<Lunch_Match__c> prevMatches = [SELECT Id, Location_Picker__c, Time_Picker__c from

Lunch_Match__c]; //construct a list of lunchers with history that holds all the previous matches and lunchers

// with no matches Map<Id, Set<Id>> prevMatchMap = getPrevMatchMap(prevMatches, activeLunchers, inactiveLunchers); List<LuncherWithHistory> lunchersWithHistory = LuncherWithHistory.convertFromMap(prevMatchMap); //do the matching List<Id> thirdWheelIds = new List<Id>(); //holds any unmatched lunchers List<Lunch_Match__c> newLunchMatches = matchup(lunchersWithHistory, lunchDate, thirdWheelIds); insert newLunchMatches; //handle the thirdwheels handleThirdWheels(thirdWheelids, newLunchMatches); }

Page 34: Coding Apex Triggers on Chatter Group Members

/** * A container class for convenience, representing a single Luncher__c, * and the set of other Luncher__c's that they have already been matched with, * based on existing Lunch_Match__c records. */

global class LuncherWithHistory implements Comparable {

public Id luncherId; //id of this luncher private Set<Id> historySet; //set of ids of other lunchers this luncher has already

//been matched with //constructor for luncher that does have match history public LuncherWithHistory(Id luncherId, Set<Id> historySet) { this.luncherId = luncherId; this.historySet = historySet; } // CompareTo() will return 0 if ids are equal, else, will return 1 if this // LuncherWithHistory's group size is larger global Integer compareTo(Object compareTo) { LuncherWithHistory compareToLuncher = (LuncherWithHistory)compareTo; if (luncherId == compareToLuncher.luncherId) return 0; if (historySet.size() > compareToLuncher.historySet.size()) return 1; return -1; }}

Page 35: Coding Apex Triggers on Chatter Group Members

//given a list of new Lunch Matches, post to each picker's chatter wall public static void notifyLunchers(List<Lunch_Match__c> matches) { //construct maps of match to time picker and match to loc picker for (Lunch_Match__c lm : matches) { matchToLuncherLoc.put(lm.Id, lm.Location_Picker__c); matchToLuncherTime.put(lm.Id, lm.Time_Picker__c); } //construct a map of luncher id to user id Map<Id, Luncher__c> luncherIdToLuncherMap = getLunchersFromMatches(matches); //build the chatter messages List<FeedItem> feedItems = new List<FeedItem>(); for (Lunch_Match__c match : matches) { Luncher__c locPicker = luncherIdToLuncherMap.get(matchToLuncherLoc.get(match.Id)); if (locPicker.Receive_Chatter_Notifications__c) { FeedItem locFeedItem = buildFeedItem(locPicker); feedItems.add(locFeedItem); } //repeat for time picker } insert feedItems; }