Coding Apex Triggers on Chatter
Group Members
A case study in coding for collaboration
Carolyn Grabill
Salesforce.com
Soft...
Safe harbor
Safe harbor statement under the Private Securities Litigation Reform Act of 1995:
This presentation may contai...
Lunchforce Technologies
▪ Using custom junction objects
▪ Triggering on Chatter Group Members
▪ Implementing an interface ...
Lunchforce brings the power
of Chatter to the real world.
Get the source: http://bit.ly/DF13Lunch
Lunchforce’s moving parts
User joins Lunchforce
Chatter group

Chatter Group
Member Trigger fires

Luncher__c record is
cr...
Lunchforce Technologies
▪ Using custom junction objects
▪ Triggering on Chatter Group Members
▪ Implementing an interface ...
Lunchforce’s Custom Objects

Junction Object
Junction Object
Records created by matching process

Luncher__c
Location Picker

Luncher__c
Time Picker

Lookup

Lunch_Match__c
Match Reco...
Lunchforce Technologies
▪ Using custom junction objects
▪ Triggering on Chatter Group Members
▪ Implementing an interface ...
Chatter Group Member Trigger
CollaborationGroupMember fields
▪ CollaborationGroupId
• The Id of the Chatter group joined

▪ CollaborationRole
• Whether...
Demo
▪ Example CollaborationGroupMember Trigger
• AddNewLunchers

▪ Trigger in action
Lunchforce Technologies
▪ Using custom junction objects
▪ Triggering on Chatter Group Members
▪ Implementing an interface ...
Lunchforce’s moving parts
User joins Lunchforce
Chatter group

Collaboration Group
Member Trigger fires

Luncher__c record...
Interfaces
▪ A template that defines methods, but does not implement them
▪ Apex class implements an interface by implemen...
Demo
▪ Example Schedulable Class
• ScheduledLunchMatch

▪ Scheduling a job in setup
Lunchforce Technologies
▪ Using custom junction objects
▪ Triggering on Chatter Group Members
▪ Implementing an interface ...
Problem: how do you maximize
new matches in a group of people,
when some of them have already
met?
Solution: sort the lunchers by how
many lunches they’ve already had.
Demo
▪ Example Comparable Class
• LuncherWithHistory

▪ Sorting a list of LuncherWithHistory records
• LunchMatcher
Lunchforce Technologies
▪ Using custom junction objects
▪ Triggering on Chatter Group Members
▪ Implementing an interface ...
Posting to Chatter from Apex using FeedItem
▪ Represents an entry in the feed, such as changes in a record feed,
including...
Demo
▪ Example FeedItem creation
• LuncherWithHistory
▪ Example weekly lunch matchup
▪ Mobile lunch match results
Go meet someone new.
Carolyn Grabill
Salesforce.com
Software Development Engineer
@CarolynCodes

Get the source: http://bit.ly/DF13Lunch
Trigger on CollaborationGroupMember
trigger addNewLunchers on CollaborationGroupMember
(after insert, after delete)
{
if (...
//create a new luncher when someone joins the lunch chatter group
public static void newLuncherOnGroupJoin(List<Collaborat...
//create new lunchers and add them to the upsert list
if (!userIdsForNewLunchers.isEmpty()) {
for (User u : [SELECT Id, Na...
//mark a luncher inactive when someone leaves the lunch chatter group
public static void makeLuncherInactiveOnGroupLeave(L...
global class scheduledLunchMatch implements Schedulable {
global void execute(SchedulableContext ctx) {
goMatchGo();
}
//s...
public boolean performScheduledMatch(DateTime lunchDate) {
//get all the active lunchers
List<Luncher__c> activeLunchers =...
/**
* A container class for convenience, representing a single Luncher__c,
* and the set of other Luncher__c's that they h...
//given a list of new Lunch Matches, post to each picker's chatter wall
public static void notifyLunchers(List<Lunch_Match...
Coding Apex Triggers on Chatter Group Members
Coding Apex Triggers on Chatter Group Members
Upcoming SlideShare
Loading in …5
×

Coding Apex Triggers on Chatter Group Members

1,616 views

Published on

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.

Published in: Technology, Business
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,616
On SlideShare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
12
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Coding Apex Triggers on Chatter Group Members

  1. 1. Coding Apex Triggers on Chatter Group Members A case study in coding for collaboration Carolyn Grabill Salesforce.com Software Development Engineer @CarolynCodes
  2. 2. Safe harbor Safe 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.
  3. 3. Lunchforce Technologies ▪ Using custom junction objects ▪ Triggering on Chatter Group Members ▪ Implementing an interface in Apex • Schedulable • Comparable ▪ Posting to Chatter from Apex
  4. 4. Lunchforce brings the power of Chatter to the real world. Get the source: http://bit.ly/DF13Lunch
  5. 5. 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
  6. 6. Lunchforce Technologies ▪ Using custom junction objects ▪ Triggering on Chatter Group Members ▪ Implementing an interface in Apex • Schedulable • Comparable ▪ Posting to Chatter from Apex
  7. 7. Lunchforce’s Custom Objects Junction Object Junction Object
  8. 8. Records created by matching process Luncher__c Location Picker Luncher__c Time Picker Lookup Lunch_Match__c Match Record Lookup
  9. 9. Lunchforce Technologies ▪ Using custom junction objects ▪ Triggering on Chatter Group Members ▪ Implementing an interface in Apex • Schedulable • Comparable ▪ Posting to Chatter from Apex
  10. 10. Chatter Group Member Trigger
  11. 11. 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
  12. 12. Demo ▪ Example CollaborationGroupMember Trigger • AddNewLunchers ▪ Trigger in action
  13. 13. Lunchforce Technologies ▪ Using custom junction objects ▪ Triggering on Chatter Group Members ▪ Implementing an interface in Apex • Schedulable • Comparable ▪ Posting to Chatter from Apex
  14. 14. 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
  15. 15. 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(); } }
  16. 16. Demo ▪ Example Schedulable Class • ScheduledLunchMatch ▪ Scheduling a job in setup
  17. 17. Lunchforce Technologies ▪ Using custom junction objects ▪ Triggering on Chatter Group Members ▪ Implementing an interface in Apex • Schedulable • Comparable ▪ Posting to Chatter from Apex
  18. 18. Problem: how do you maximize new matches in a group of people, when some of them have already met?
  19. 19. Solution: sort the lunchers by how many lunches they’ve already had.
  20. 20. Demo ▪ Example Comparable Class • LuncherWithHistory ▪ Sorting a list of LuncherWithHistory records • LunchMatcher
  21. 21. Lunchforce Technologies ▪ Using custom junction objects ▪ Triggering on Chatter Group Members ▪ Implementing an interface in Apex • Schedulable • Comparable ▪ Posting to Chatter from Apex
  22. 22. 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;
  23. 23. Demo ▪ Example FeedItem creation • LuncherWithHistory ▪ Example weekly lunch matchup ▪ Mobile lunch match results
  24. 24. Go meet someone new.
  25. 25. Carolyn Grabill Salesforce.com Software Development Engineer @CarolynCodes Get the source: http://bit.ly/DF13Lunch
  26. 26. Trigger on CollaborationGroupMember trigger 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); } }
  27. 27. //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); } }
  28. 28. //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; } } }
  29. 29. //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; } } }
  30. 30. 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); } }
  31. 31. 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); }
  32. 32. /** * 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; } }
  33. 33. //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; }

×