Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Facebook Outlook Add-in

  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 Smiley

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 Smiley

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:  
  40:    //look through each facebook friend associated with an outlook contact
  41:    //and search the incoming post to match keywords
  42:    foreach (FacebookDBDataSet.FacebookDataRow row in fdDT.Rows)
  43:    {
  44:        if (IsKeywordFound(row.FavoriteTVShows, postItem, out keyword))
  45:        {
  46:            entryIDs.Add(row.EntryID);
  47:  
  48:  
  49:            continue;
  50:        }
  51:     //more code
  52:     }
  53:  
  54:     //more code
  55: }
  56:  
  57: private bool IsKeywordFound(string keywords, Outlook.PostItem postItem, out string matchedKeyword)
  58: {
  59:     bool isFound = false;
  60:     matchedKeyword = string.Empty;
  61:  
  62:     if (!String.IsNullOrEmpty(keywords))
  63:     {
  64:         string[] keywordList = keywords.Split(',');
  65:  
  66:         //take the tokenized facebook data and search the post for matching keywords
  67:         //not the most sophisticated search, can be improved in later versions
  68:         foreach (string keyword in keywordList)
  69:         {
  70:             string keywordSearch = keyword.ToUpperInvariant().Trim();
  71:             if (postItem.Body.ToUpperInvariant().Contains(keywordSearch) || postItem.Subject.ToUpperInvariant().Contains(keywordSearch))
  72:             {
  73:                 //exclude and keywords that the user has added to the settings screen to exclude
  74:                 if (!Properties.Settings.Default.ExcludeList.ToUpperInvariant().Contains(keywordSearch))
  75:                 {
  76:                     isFound = true;
  77:                     matchedKeyword = keyword;
  78:                     break;
  79:                 }
  80:             }
  81:         }
  82:     }
  83:  
  84:     return isFound;
  85: }

 

 

VB.NET

   1: Private Sub Items_ItemAdd(ByVal Item As Object)
   2:     Dim post As Outlook.PostItem = TryCast(Item, Outlook.PostItem)
   3:  
   4:     If Not post Is Nothing Then
   5:         Dim syncThread As System.Threading.Thread = New System.Threading.Thread(New System.Threading.ParameterizedThreadStart(AddressOf SearchPostAsync))
   6:         syncThread.IsBackground = True
   7:         syncThread.SetApartmentState(System.Threading.ApartmentState.STA)
   8:         syncThread.Start(post)
   9:     End If
  10: End Sub
  11:  
  12: ''' <summary>
  13: ''' Begins async search of an incoming post
  14: ''' </summary>
  15: ''' <param name="item"></param>
  16: Private Sub SearchPostAsync(ByVal item As Object)
  17:     Dim post As Outlook.PostItem = TryCast(item, Outlook.PostItem)
  18:  
  19:     If Not post Is Nothing Then
  20:         Dim ps As PostSearcher = New PostSearcher()
  21:         ps.SearchPost(post, _contacts)
  22:     End If
  23: End Sub
  24:  
  25: Public Sub SearchPost(ByVal postItem As Outlook.PostItem, ByVal contacts As Outlook.Items)
  26:  
  27:     Dim fdTA As FacebookDBDataSetTableAdapters.FacebookDataTableAdapter = 
  28:         New FacebookDBDataSetTableAdapters.FacebookDataTableAdapter()
  29:  
  30:     Dim fdDT As FacebookDBDataSet.FacebookDataDataTable = fdTA.GetData()
  31:  
  32:     Dim entryIDs As List(Of String) = New List(Of String)()
  33:     Dim keyword As String = String.Empty
  34:  
  35:     'look through each facebook friend associated with an outlook contact
  36:     'and search the incoming post to match keywords
  37:     For Each row As FacebookDBDataSet.FacebookDataRow In fdDT.Rows
  38:         If IsKeywordFound(row.FavoriteTVShows, postItem, keyword) Then
  39:             entryIDs.Add(row.EntryID)
  40:  
  41:             Continue For
  42:         End If
  43:         'more code.....
  44:     Next row
  45:  
  46: 'more code.....
  47: End Sub
  48:  
  49: ''' <summary>
  50: ''' search the post for a keyword string
  51: ''' </summary>
  52: ''' <param name="keywords"></param>
  53: ''' <param name="postItem"></param>
  54: ''' <param name="matchedKeyword"></param>
  55: ''' <returns></returns>
  56: Private Function IsKeywordFound(ByVal keywords As String, ByVal postItem As Outlook.PostItem, <System.Runtime.InteropServices.Out()> ByRef matchedKeyword As String) As Boolean
  57:     Dim isFound As Boolean = False
  58:     matchedKeyword = String.Empty
  59:  
  60:     If (Not String.IsNullOrEmpty(keywords)) Then
  61:         Dim keywordList As String() = keywords.Split(","c)
  62:  
  63:         'take the tokenized facebook data and search the post for matching keywords
  64:         'not the most sophisticated search, can be improved in later versions
  65:         For Each keyword As String In keywordList
  66:             Dim keywordSearch As String = keyword.ToUpperInvariant().Trim()
  67:             If postItem.Body.ToUpperInvariant().Contains(keywordSearch) OrElse postItem.Subject.ToUpperInvariant().Contains(keywordSearch) Then
  68:                 'exclude and keywords that the user has added to the settings screen to exclude
  69:                 If (Not My.Settings.ExcludeList.ToUpperInvariant().Contains(keywordSearch)) Then
  70:                     isFound = True
  71:                     matchedKeyword = keyword
  72:                     Exit For
  73:                 End If
  74:             End If
  75:         Next keyword
  76:     End If
  77:  
  78:     Return isFound
  79: End Function

 

Creating the Installer

When you create a new Outlook Add-in project, Visual Studio creates a setup project along with it.  The default setup project needs a little tweaking before you can distribute to your friends to install. Chris Castillo wrote an excellent post on building setup projects for office add-ins.  A few minor challenges are granting full trust, installing / checking additional prerequisites like VSTO 2005 SE for Office 2007 / PIAs and creating the right registry entries.  If something doesn't work with the installer in this project, I blame him Smiley 

 

Conclusion

The add-in for this article is just one example of the fun things you can do with some of the API blocks that are being created by the Visual Studio Express team and others.  The Facebook toolkit in particular can be used to create applications within Facebook on the web or in stand-alone desktop applications like this add-in.  I plan on adding this code into the Facebook Developer Toolkit project on CodePlex as a sample at some point so anyone can help expand this add-in.  Some future things I think would be cool to add are photo album exporting, more sophisticated keyword searching, syncing of contact info and email synchronization.  Some of those are dependent on the API changing and may not be possible.

If you'd like to contact me, feel free to send me a message or add me as a friend on Facebook.

Follow the Discussion

  • Jamie AkersJamie Akers

    I've written a similar app but it takes user photos from Facebook and attaches them to Outlook Contacts.  When sync'd to a Windows Mobile device you get a nice clear photo of who is calling you!

  • bizguybizguy

    great, i like it.

    Facebook becomes so popular now

  • Noticias externasNoticias externas

    Facebook is a social utility that connects people with friends and others who work, study and live around

  • 10,000 Monkeys - Harnessing the Power of Typing Monkeys10,000 Monkeys - Harnessing the Power of Typing Monkeys

    Writing blog posts just keeps jumping down the list. I think I need to realize that regular blogging

  • SamratSamrat

    I had to give it a try.... Nice add-on..

  • Clint RutkasClint I'm a "developer"

    @dasx, I just downloaded and installed it on my computer just fine.  Do you have the install folder fully unzipped?

    I'm on Win7 x64 with Office 2010 beta installed.

  • dasxdasx

    it didnt install it gives a connection error

    An error occurred downloading the following resource:

    http://go.microsoft.com/fwlink/?LinkId=79951

    Date: 12/4/2009 10:19:43 PM

    See the setup log file located at 'C:\DOCUME~1\Dasx\LOCALS~1\Temp\VSD23A1.tmp\install.log' for more information.

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.