Facebook Outlook Add-in

Published 30 August 07 09:37 PM | Coding4Fun 
  Facebook is a social utility that connects people with friends and others who work, study and live around them. Using the Facebook Developer Toolkit, you can combine the data stored on Facebook with contacts already stored in Outlook via a custom form region. Additionally, using VSTO and the Outlook object model, you can monitor incoming RSS feeds for posts that match interests of your friends on Facebook.
Clarity Consulting

Difficulty: Intermediate
Time Required: 6-10 hours
Cost: Free
Software: Outlook 2007, Visual Basic or Visual C# Express Editions, Microsoft SQL Server 2005 Compact Edition Microsoft Visual Studio 2005 Tools for the 2007 Microsoft Office System Facebook Developer Toolkit
Hardware:
Download: Download
 

As Facebook becomes more widely used by corporate users a.k.a older people like me, I thought it would be cool if I could link Facebook data into Outlook.  Originally I wanted to build something similar to the Plaxo Toolbar for Outlook to sync Facebook contact info with my Outlook contacts, but unfortunately  the Facebook API doesn't allow you to retrieve contact info. (If anyone from Facebook is reading this, contact info in the API would be awesome :)

So what does the Facebook Outlook Add-in do? Two things.  First, you can synchronize Facebook data available via the API like Favorite TV Shows, Movies, etc. to an Outlook contact by linking a contact with his/her Facebook ID (The ID appears in the url on profile pages i.e. http://www.facebook.com/profile.php?id=828485692).  After a contact is linked to his/her Facebook profile, profile data is then displayed on a custom region in the Outlook contact form (Figure 1).  The data is stored in a local SQL Server 2005 Compact Edition DB and periodically refreshed from Facebook.

image

Figure 1: Custom Contact Region

 

The second feature is RSS feed monitoring for articles that match interests of your Facebook friends.  For example, I subscribe to Pitchfork's record review and one of my friends on Facebook likes Iron & Wine.  If Pitchfork reviews a new Iron & Wine album, the add-in will flag the post for follow-up to notify me that my friend might be interested in that post.  A custom region on the RSS post displays which friends matched and why (Figure 2).  In a class I once attended on relationship management (business relationships and networking....not dating.  I think my girlfriend already manages me quite well), the speaker mentioned that it's important to keep relationships fresh.  If I see an article that is potentially interesting to one of my contacts, it's a good excuse to email them and keep the relationship current.  Normally I'd have to remember what everyone likes, but Facebook allows me to be lazy.

 

image

Figure 2: Custom RSS Post Region

 

Getting Started

If you'd like to follow along at home and debug the source code, you first need to install Microsoft SQL Server 2005 Compact Edition and the Microsoft Visual Studio 2005 Tools for the 2007 Microsoft Office System.  Also, when building Outlook add-ins, there are several registry entries that need to be created.  To debug the source code for this article you must run all of the .reg files in the "RegistryEntries" folder inside "src\FBOutlookCS" or "src\FBOutlookVB". 

If you just want to try out the add-in without messing around with the source code, first install Microsoft SQL Server 2005 Compact Edition then run "setup.exe" in the "install" folder from the downloaded code of this article.

 

Creating a Custom From Region in Outlook

I previously posted another add-in article on Coding4Fun, Collecting Outlook 2007 Statistics Using VSTO 2005 SE.  The steps I used to create this add-in are basically the same.  One difference this time was instead of adding registry entries to point to the custom region's manifest file, the registry entry just points to the actual add-in (Figure 3).  The add-in then dynamically loads the proper custom region from the project's resources based on the which form was requested.

image

Figure 3: Registry Settings

 

C#

   1: /// <summary>
   2: /// Implemented from the FormRegionStartup interface, GetFormRegionManifest is called when the
   3: /// Form Region manifest information is requested by Outlook.  This can happen from a number of
   4: /// trigger points in Outlook.  Once the manifest information is loaded, Outlook will not ask for 
   5: /// this information again during the same Outlook session.
   6: /// </summary>
   7: /// <param name="FormRegionName">Name of the form region, as provided by the registry key</param>
   8: /// <param name="LCID">Language ID for the Office UI language</param>
   9: /// <returns>A string value containing the XML contents of the form region manifest</returns>
  10: public object GetFormRegionManifest(string FormRegionName, int LCID)
  11: {
  12:     switch (FormRegionName)
  13:     {
  14:         case CONTACT_REGION:
  15:             return Properties.Resources.FacebookContactRegionManifest;
  16:  
  17:         case RSS_REGION:
  18:             return Properties.Resources.FacebookRSSRegionManifest;
  19:  
  20:         default:
  21:             return null;
  22:     }
  23: }

VB.NET

   1: Public Function GetFormRegionManifest(ByVal FormRegionName As String, ByVal LCID As Integer) As Object Implements Microsoft.Office.Interop.Outlook._FormRegionStartup.GetFormRegionManifest
   2:     Select Case FormRegionName
   3:         Case CONTACT_REGION
   4:             Return My.Resources.facebookcontactregionmanifest
   5:  
   6:         Case RSS_REGION
   7:             Return My.Resources.facebookrssregionmanifest
   8:  
   9:         Case Else
  10:             Return Nothing
  11:     End Select
  12: End Function

 

Synchronizing Data with Facebook

Facebook has a REST API for retrieving work & school history, photos, events and personal info (Basically everything except contact info).  When building your own Facebook application, you must first get an API key.  There are a variety of ways to call the Facebook web services.  You could use XSD, Linq, or the .NET Facebook Developer Toolkit.  I chose the Facebook Developer Toolkit which has a strongly typed object model for Facebook data, simplifies web service calls, handles authentication / session management and is a pleasure to use.  Plus I helped build the toolkit so I was looking for a chance to promote it :)

After adding a reference to the toolkit, you can retrieve Facebook data and store it in a SQL Server Compact DB with just a few lines of code.

C#

   1: _fbService = new Facebook.Components.FacebookService();
   2: _fbService.ApplicationKey = APP_KEY;
   3: _fbService.Secret = SECRET;
   4:  
   5: Collection<Facebook.Entity.User> users = _fbService.GetUserInfo(facebookID);
   6:  
   7: FacebookDBDataSetTableAdapters.FacebookDataTableAdapter fbTA =
   8:     new FacebookDBDataSetTableAdapters.FacebookDataTableAdapter();
   9:  
  10: fbTA.Insert(facebookID, users[0].AboutMe,
  11:     users[0].Interests, users[0].Activities, users[0].Movies,
  12:     users[0].TVShows, users[0].Books, entryID);

 

VB.NET

   1: _fbService = New Facebook.Components.FacebookService()
   2: _fbService.ApplicationKey = API_KEY
   3: _fbService.Secret = SECRET
   4:  
   5: Dim users As Collection(Of Facebook.Entity.User) = 
   6:     _fbService.GetUserInfo(facebookID)
   7:  
   8: Dim fbTA As FacebookDBDataSetTableAdapters.FacebookDataTableAdapter = 
   9:     New FacebookDBDataSetTableAdapters.FacebookDataTableAdapter()
  10:  
  11: fbTA.Update(facebookID, users[0].AboutMe,
  12:     users[0].Interests, users[0].Activities, users[0].Movies,
  13:     users[0].TVShows, users[0].Books, entryID, entryID);

 

The table adapters used in the code above were generated from a dataset linked to the database.  The SQL Server Compact DB is a lightweight database that can be deployed in a single file.  The database doesn't run any services like SQL Express and is a svelte 35KB when empty.

image

Figure 4: Facebook Local DB Dataset

 

Based on a user configurable setting, the add-in will periodically synchronize all Facebook data for each contact.  The sync process is controlled through a timer and the actual synchronization runs asynchronously on a background thread to keep the Outlook UI responsive.  If the UI freezes, it's probably that other add-in you installed.

C#

   1: /// <summary>
   2: /// Begins the sync timer
   4: private void InitializeSyncTimer()
   5: {
   6:     _syncTimer = new Timer();
   7:     _syncTimer.Enabled = true;
   8:     _syncTimer.Interval = SyncIntervalInMiliseconds;
   9:     _syncTimer.Start();
  10: }
  11:  
  12: /// <summary>
  13: /// Event handler for the sync interval timer.  Starts the sync to facebook async
  14: /// </summary>
  15: /// <param name="sender"></param>
  16: /// <param name="e"></param>
  17: private void syncTimer_Tick(object sender, EventArgs e)
  18: {
  19:     StartBackgroundSync();
  20:     _syncTimer.Interval = SyncIntervalInMiliseconds;
  21: }
  22:  
  23: /// <summary>
  24: /// Starts an asynchronous sync of facebook friend data
  25: /// </summary>
  26: private void StartBackgroundSync()
  27: {
  28:     System.Threading.Thread syncThread = new System.Threading.Thread(new System.Threading.ThreadStart(SyncUsersWithFacebook));
  29:     syncThread.IsBackground = true;
  30:     syncThread.SetApartmentState(System.Threading.ApartmentState.STA);
  31:     syncThread.Start();
  32: }
  33:  
  34: /// <summary>
  35: /// Synchronizes facebook data of facebook friend data to the sql compact db
  36: /// </summary>
  37: private void SyncUsersWithFacebook()
  38: {
  39:     FBSync.Instance.SyncUsersWithFacebookAsync();
  40: }

 

VB.NET

   1: ''' <summary>
   2: ''' Begins the sync timer
   3: ''' </summary>
   4: Private Sub InitializeSyncTimer()
   5:     _syncTimer = New Timer()
   6:     _syncTimer.Enabled = True
   7:     _syncTimer.Interval = SyncIntervalInMiliseconds
   8:     _syncTimer.Start()
   9: End Sub
  10:  
  11: ''' <summary>
  12: ''' Event handler for the sync interval timer.  Starts the sync to facebook async
  13: ''' </summary>
  14: ''' <param name="sender"></param>
  15: ''' <param name="e"></param>
  16: Private Sub syncTimer_Tick(ByVal sender As Object, ByVal e As EventArgs)
  17:     StartBackgroundSync()
  18:     _syncTimer.Interval = SyncIntervalInMiliseconds
  19: End Sub
  20:  
  21: ''' <summary>
  22: ''' Starts an asynchronous sync of facebook friend data
  23: ''' </summary>
  24: Private Sub StartBackgroundSync()
  25:     Dim syncThread As System.Threading.Thread = New System.Threading.Thread(New System.Threading.ThreadStart(AddressOf SyncUsersWithFacebook))
  26:     syncThread.IsBackground = True
  27:     syncThread.SetApartmentState(System.Threading.ApartmentState.STA)
  28:     syncThread.Start()
  29: End Sub
  30:  
  31: ''' <summary>
  32: ''' Synchronizes facebook data of facebook friend data to the sql compact db
  33: ''' </summary>
  34: Private Sub SyncUsersWithFacebook()
  35:     FBSync.Instance.SyncUsersWithFacebookAsync()
  36: End Sub
  37:  

 

Searching Posts for Keywords

To monitor incoming RSS posts first you need to wire up the ItemAdd events on each sub-folder of the RSS feeds folder in Outlook.  There are thousands more events that you can handle in addition to ItemAdd.  My goal is to keep writing Coding4Fun articles until I use them all. 

You also need to wire up an event for adding sub-folders to the RSS feed folder (so you can dynamically add more event handlers for the previously mentioned ItemAdd events)  That way if a new feed is added to Outlook, the add-in will know to monitor it.  A code snippet might illustrate better what is happening.

C#

   1: /// <summary>
   2: private Outlook.MAPIFolder _rssFeedRootFolder;
   3: private Outlook.Folders _rssFeedFolders;
   4:  
   5: _rssFeedFolders.FolderAdd +=
   6:     new Microsoft.Office.Interop.Outlook.FoldersEvents_FolderAddEventHandler(_rssFeedFolders_FolderAdd);
   7:  
   8: foreach (Outlook.Folder rssFolder in _rssFeedFolders)
   9: {
  10:     AddRSSFolderHandlers(rssFolder.Items);
  11: }
  12:  
  13: /// Add a handler to a RSS folder to watch incoming posts
  14: /// </summary>
  15: /// <param name="items"></param>
  16: private void AddRSSFolderHandlers(Outlook.Items items)
  17: {
  18:     _rssFolderItems.Add(items);
  19:     _rssFolderItems[_rssFolderItems.Count - 1].ItemAdd +=
  20:         new Microsoft.Office.Interop.Outlook.ItemsEvents_ItemAddEventHandler(Items_ItemAdd);
  21:     _rssFolderItems[_rssFolderItems.Count - 1].ItemRemove +=
  22:         new Microsoft.Office.Interop.Outlook.ItemsEvents_ItemRemoveEventHandler(Items_ItemRemove);
  23: }
  24:  
  25: void _rssFeedFolders_FolderAdd(Microsoft.Office.Interop.Outlook.MAPIFolder Folder)
  26: {
  27:     AddRSSFolderHandlers(Folder.Items);
  28: }

 

VB.NET

   1: Private _rssFeedRootFolder As Outlook.MAPIFolder
   2: Private _rssFeedFolders As Outlook.Folders
   3:  
   4: AddHandler _rssFeedFolders.FolderAdd, AddressOf _rssFeedFolders_FolderAdd
   5:  
   6: For Each rssFolder As Outlook.Folder In _rssFeedFolders
   7:     AddRSSFolderHandlers(rssFolder.Items)
   8: Next rssFolder
   9:  
  10: Private Sub _rssFeedFolders_FolderAdd(ByVal Folder As Microsoft.Office.Interop.Outlook.MAPIFolder)
  11:     AddRSSFolderHandlers(Folder.Items)
  12: End Sub
  13:  
  14: ''' <summary>
  15: ''' Add a handler to a RSS folder to watch incoming posts
  16: ''' </summary>
  17: ''' <param name="items"></param>
  18: Private Sub AddRSSFolderHandlers(ByVal items As Outlook.Items)
  19:     _rssFolderItems.Add(items)
  20:     AddHandler _rssFolderItems(_rssFolderItems.Count - 1).ItemAdd, AddressOf Items_ItemAdd
  21:     AddHandler _rssFolderItems(_rssFolderItems.Count - 1).ItemRemove, AddressOf Items_ItemRemove
  22: End Sub

 

When a new RSS post comes into Outlook, the ItemAdd event fires and the add-in launches a search of the post on a background thread.  Searching of a post involves looping through each stored Facebook profile and tokenizing the personal info fields into keywords based on commas.  As your list of friend's grows, there might be a more a efficient way of storing keywords and searching posts.  I'm shy so I only have a handful of friends on Facebook. 

Another feature is the ability to exclude certain keywords (Figure 5).  Suppose someone likes the rapper "Common", but that word might appear in unrelated posts.  In the add-in settings you can add words to an exclusion list to prevent false matches.  Obviously the searching function is pretty simple, but it works for the purposes of this sample.

 

image

Figure 5: Add-in Settings

 

Below are some snippets of code for searching incoming posts.

C#

   1: void Items_ItemAdd(object Item)
   2: {
   3:     Outlook.PostItem post = Item as Outlook.PostItem;
   4:  
   5:     if (post != null)
   6:     {
   7:         System.Threading.Thread syncThread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(SearchPostAsync));
   8:         syncThread.IsBackground = true;
   9:         syncThread.SetApartmentState(System.Threading.ApartmentState.STA);
  10:         syncThread.Start(post);
  11:     }
  12: }
  13:  
  14: /// <summary>
  15: /// Begins async search of an incoming post
  16: /// </summary>
  17: /// <param name="item"></param>
  18: private void SearchPostAsync(object item)
  19: {
  20:     Outlook.PostItem post = item as Outlook.PostItem;
  21:  
  22:     if (post != null)
  23:     {
  24:         PostSearcher ps = new PostSearcher();
  25:         ps.SearchPost(post, _contacts);
  26:     }
  27: }
  28:  
  29: public void SearchPost(Outlook.PostItem postItem, Outlook.Items contacts)
  30: {
  31:  
  32:    FacebookDBDataSetTableAdapters.FacebookDataTableAdapter fdTA = 
  33:        new FacebookDBDataSetTableAdapters.FacebookDataTableAdapter();
  34:  
  35:    FacebookDBDataSet.FacebookDataDataTable fdDT = fdTA.GetData();
  36:  
  37:    List<string> entryIDs = new List<string>();
  38:    string keyword = string.Empty;
  39: