Pieter van der Westhuizen

How to integrate Outlook add-in with Freshbooks web-service, part 1

Freshbooks is a cloud-based accounting web-service that allows more than 5 million people to capture invoices, expenses and time sheets online. It also provides a rich API which developers can use to access Freshbooks data using HTTP and XML.

In this article, the first of a series, we’ll start building a Microsoft Outlook Add-in that will serve as a client for Freshbooks. Users would be able to view their Freshbooks data as well as perform numerous Freshbooks tasks all from within Outlook.

We’ll cover the following topics in this article:

Creating the Add-in Express Outlook add-in

We’ll start by creating a new ADX COM Add-in project in Visual Studio using Add-in Express for Office and .net.

This will start the “New Microsoft Office COM Add-in” wizard. Select your preferred programming language (VB.NET, C#, C++) as well as the minimum supported version of MS Office you need your add-in to support. Since we’ll use a Solution Module for our add-in, we’ll select Microsoft Office 2010 as our minimum supported version.

Add-in Express enables you to create one add-in that targets multiple MS Office Applications, when prompted, select Microsoft Outlook as the supported application for this add-in.

Create Freshbooks web-service specific Outlook folders

Freshbooks has a number of different entity/record types, but to show how to integrate our Outlook Add-in with it, we’ll focus on the following web-service items:

  • Clients;
  • Staff;
  • Invoices;
  • Projects;
  • Tasks; and
  • Timesheets.

One part of our vision for this add-in, is that the user would be able to see their Freshbooks data directly from inside MS Outlook. This means we’ll need to create a folder in Outlook for each one of the above mentioned types.

In order to create the necessary folder, we’ll create a method called CreateFreshbooksFolders. This method will check whether a folder called Freshbooks already exists in the root folder’s folders, if not it will create a folder for each of the entities. The code for this method follows below:

private void CreateFreshbooksFolders()
{
    Outlook._NameSpace session = null;
    Outlook._Store defaultStore = null;
    Outlook.Folder rootFolder = null;
 
    Outlook.Folder freshbooksFolder = null;
    Outlook.Folders rootFolders = null;
 
    try
    {
        session = OutlookApp.GetNamespace("MAPI");
        defaultStore = session.DefaultStore;
        rootFolder = defaultStore.GetRootFolder() as Outlook.Folder;
 
        freshbooksFolder = GetFolder(rootFolder.FolderPath + @"\Freshbooks");
        if (freshbooksFolder == null)
        {
            rootFolders = rootFolder.Folders;
            freshbooksFolder = rootFolders.Add("Freshbooks", Outlook.OlDefaultFolders.olFolderInbox) as Outlook.Folder;
            CreateFolder(freshbooksFolder, "Clients", Outlook.OlDefaultFolders.olFolderContacts, "IPM.Contact.Freshbooks.Client", "Client", true);
            CreateFolder(freshbooksFolder, "Staff", Outlook.OlDefaultFolders.olFolderContacts, "IPM.Contact.Freshbooks.Staff", "Team Member", true);
            CreateFolder(freshbooksFolder, "Invoices", Outlook.OlDefaultFolders.olFolderNotes, "IPM.StickyNote.Freshbooks.Invoice", "Invoice");
            CreateFolder(freshbooksFolder, "Projects", Outlook.OlDefaultFolders.olFolderContacts, "IPM.Contact.Freshbooks.Project", "Project", true);
            CreateFolder(freshbooksFolder, "Tasks", Outlook.OlDefaultFolders.olFolderTasks, "IPM.Task.Freshbooks.Task", "Task");
            CreateFolder(freshbooksFolder, "Timesheets", Outlook.OlDefaultFolders.olFolderCalendar, "IPM.Appointment.Freshbooks.Timesheet", "Timesheet");
        }
    }
    finally
    {
        if (rootFolders != null) Marshal.ReleaseComObject(rootFolders);
        if (freshbooksFolder != null) Marshal.ReleaseComObject(freshbooksFolder);
        if (rootFolder != null) Marshal.ReleaseComObject(rootFolder);
        if (defaultStore != null) Marshal.ReleaseComObject(defaultStore);
        if (session != null) Marshal.ReleaseComObject(session);
    }
}

You will notice that we used a helper method to get a folder based on its path, called GetFolder. The code for this method looks like the following:

public Outlook.Folder GetFolder(string folderPath)
{
    Outlook._Application outlookApp = null;
    Outlook.Folder returnFolder = null;
    Outlook.Folders subFolders = null;
    Outlook.NameSpace session = null;
    Outlook.Folders sessionFolders = null;
 
    try
    {
        outlookApp = AddinModule.CurrentInstance.OutlookApp;
        session = outlookApp.Session;
        folderPath = folderPath.TrimStart("\\".ToCharArray());
 
        String[] folders = folderPath.Split("\\".ToCharArray());
        sessionFolders = session.Folders;
        returnFolder = sessionFolders[folders[0]] as Outlook.Folder;
 
        if (returnFolder != null)
        {
            String folderName;
            for (int i = 1; i < folders.Length; i++)
            {
                folderName = folders[i];
                subFolders = returnFolder.Folders;
                returnFolder = subFolders[folderName] as Outlook.Folder;
                if (subFolders != null) Marshal.ReleaseComObject(subFolders);
            }
        }
    }
    catch
    {
        returnFolder = null;
    }
    finally
    {
        if (sessionFolders != null) Marshal.ReleaseComObject(sessionFolders);
        if (session != null) Marshal.ReleaseComObject(session);
        if (subFolders != null) Marshal.ReleaseComObject(subFolders);
    }
    return returnFolder;
}

We created each individual folder (to display the web-service data in the Outlook UI) using the CreateFolder method. This method accepts 6 parameters, namely:

  • rootFolder – A reference to the Root Folder to which the folder will be added.
  • folderName – The name of the folder e.g. Clients.
  • folderType – The folder type uses a standard Outlook.OlDefaultFolders enumeration value.
  • messageClass – The message class of the folder e.g. IPM.Contact.Freshbooks.Client.
  • displayName – This is the display name that will be shown inside the folder e.g. Client.
  • markAsAddressBook – A Boolean value specifying whether the folder should be shown as an Outlook address book.

The full code listing for the CreateFolder method follows herewith:

private void CreateFolder(Outlook.Folder rootFolder, string folderName, Outlook.OlDefaultFolders folderType, string messageClass, string displayName, bool markAsAddressBook = false)
{
    Outlook.Folder folder = null;
    Outlook.Folders folders = null;
    Outlook._PropertyAccessor propertyAccessor = null;
 
    try
    {
 
        folder = GetFolder(rootFolder.FolderPath + "\\" + folderName);
 
        if (folder == null)
        {
            folders = rootFolder.Folders;
            folder = folders.Add(folderName, folderType) as Outlook.Folder;
            folder.ShowAsOutlookAB = markAsAddressBook;
            folder.CustomViewsOnly = true;
            propertyAccessor = folder.PropertyAccessor;
            propertyAccessor.SetProperty("https://schemas.microsoft.com/mapi/proptag/0x36E5001F", messageClass);
            propertyAccessor.SetProperty("https://schemas.microsoft.com/mapi/proptag/0x36E6001E", displayName);
        }
    }
    finally
    {
        if (folder != null) Marshal.ReleaseComObject(folder);
        if (propertyAccessor != null) Marshal.ReleaseComObject(propertyAccessor);
        if (folders != null) Marshal.ReleaseComObject(folders);
    }
}

Setting custom MessageClass names for the web-service’s folders

Note, that in the CreateFolder method we used the PropertyAccessor object to set the folder’s message class and display name. The schema name that was used to specify the message class, is https://schemas.microsoft.com/mapi/proptag/0x36E5001F

In order to specify the display name for the folder you would need to use the following schema name:

https://schemas.microsoft.com/mapi/proptag/0x36E6001E

By setting the folders’ message class and display name, you’ll notice that the “When posting to this folder, use:” drop down list, on the folder properties window, now shows the Display Name property as the selected item. In the following image, I right-clicked on the Timesheets sub-folder inside the Freshbooks folder and selected properties from the context-menu.

Adding the web-service data to Outlook Address books

The CreateFolder method also gives us the option to mark the Freshbooks web-service folder as an Outlook Address book, by setting the markAsAddressBook parameters to true. This in turn sets the ShowAsOutlookAB property of the Outlook Folder object to true.

The result inside Outlook, when choosing a recipient for an e-mail by clicking on the To… button, would then list the folders as Outlook Address books, as illustrated below:

Creating a Freshbooks Solution Module in the Outlook add-in

We’ll create an Outlook Solution Module to group all the web-service functionality together. To do this, first open the AddinModule designer surface of your Outlook add-in project. Add a new ADXOLSolutionModule component to the AddinModule by clicking on its corresponding button in the toolbar.

Rename the newly added ADXOLSolutionModule to freshbooksSolutionModule. We won’t add any item to the component using the Add-in Express designer tools, instead we’ll add everything using code. Before we switch back to the code, we first need to add a standard ImageList control to the AddinModule designer surface and add the icons we’ll need for the Solutions Module to it.

I’ve added a few Freshbooks specific icons to it, all of which were created using the free SyncFusion Metro Studio.

Adding the Freshbooks specific folders to the Solution Module

Add the following method, called CreateSolutionModuleLayout that your Outlook add-in will use to create web-service specific folders:

private void CreateSolutionModuleLayout()
{
    ADXOLSolutionFolder freshbooksFolder = new ADXOLSolutionFolder
    {
        FolderName = "Freshbooks",
        FolderType = ADXOLSolutionFolderType.Inbox,
        ImageList = iml16x16Icons,
        Image = 0
    };
 
    ADXOLSolutionFolder freshbooksClientsFolder = new ADXOLSolutionFolder
    {
        FolderName = "Clients",
        FolderType = ADXOLSolutionFolderType.Contacts,
        ImageTransparentColor = System.Drawing.Color.Transparent,
        ImageList = iml16x16Icons,
        Image = 1
    };
 
    ADXOLSolutionFolder freshbooksStaffFolder = new ADXOLSolutionFolder
    {
        FolderName = "Staff",
        FolderType = ADXOLSolutionFolderType.Contacts,
        ImageTransparentColor = System.Drawing.Color.Transparent,
        ImageList = iml16x16Icons,
        Image = 2
    };
 
    ADXOLSolutionFolder freshbooksInvoiceFolder = new ADXOLSolutionFolder
    {
        FolderName = "Invoices",
        FolderType = ADXOLSolutionFolderType.Tasks,
        ImageTransparentColor = System.Drawing.Color.Transparent,
        ImageList = iml16x16Icons,
        Image = 3
    };
 
    ADXOLSolutionFolder freshbooksProjectsFolder = new ADXOLSolutionFolder
    {
        FolderName = "Projects",
        FolderType = ADXOLSolutionFolderType.Contacts,
        ImageTransparentColor = System.Drawing.Color.Transparent,
        ImageList = iml16x16Icons,
        Image = 4
    };
 
    ADXOLSolutionFolder freshbooksTasksFolder = new ADXOLSolutionFolder
    {
        FolderName = "Tasks",
        FolderType = ADXOLSolutionFolderType.Tasks,
        ImageTransparentColor = System.Drawing.Color.Transparent,
        ImageList = iml16x16Icons,
        Image = 5
    };
 
    ADXOLSolutionFolder freshbooksTimesheetsFolder = new ADXOLSolutionFolder
    {
        FolderName = "Timesheets",
        FolderType = ADXOLSolutionFolderType.Calendar,
        ImageTransparentColor = System.Drawing.Color.Transparent,
        ImageList = iml16x16Icons,
        Image = 6
    };
 
    freshbooksFolder.Folders.Add(freshbooksClientsFolder);
    freshbooksFolder.Folders.Add(freshbooksStaffFolder);
    freshbooksFolder.Folders.Add(freshbooksInvoiceFolder);
    freshbooksFolder.Folders.Add(freshbooksProjectsFolder);
    freshbooksFolder.Folders.Add(freshbooksTasksFolder);
    freshbooksFolder.Folders.Add(freshbooksTimesheetsFolder);
    freshbooksSolutionModule.Folders.Add(freshbooksFolder);
}

The preceding code creates a Solution folder for each of the Freshbooks folders we’ve created earlier, sets their folder types as well as their icons. It then adds all of them to the Solution Module we’ve added earlier.

The last step we need to do is to call both the CreateFreshbooksFolders and CreateSolutionModuleLayout methods when the add-in starts. To do this, we need to create an event handler for the AddinStartupComplete event. Switch to the AddinModule designer surface and double-click next to its AddinStartupComplete event in the properties window’s list of events.

This will generate an empty event handler. Add the following code to it:

private void AddinModule_AddinStartupComplete(object sender, EventArgs e)
{
    CreateFreshbooksFolders();
    CreateSolutionModuleLayout();
}

Build, register and run your project and you should see a Freshbooks Solution Module integrated into Outlook containing all the Freshbooks folders.

That concludes part 1 of the “Integrating an Outlook add-in with Freshbooks web-service”. In the next article, we’ll cover:

  • Create Custom Views for the Freshbooks specific folders;
  • Import Freshbooks data into Outlook Folders; and
  • Replacing the Outlook Inspector UI with a custom Freshbooks specific look.

Thank you for reading. Until next time, keep coding!

Available downloads:

This sample Outlook add-in was developed using Add-in Express for Office and .net:

Freshbooks Outlook add-in (C#)

How to integrate Outlook add-in with the Freshbooks web-service

Post a comment

Have any questions? Ask us right now!