Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / productivity / Office

Outlook 2010 Add-in Custom Classification and Attribution of Emails

4.67/5 (7 votes)
21 Mar 2012Apache11 min read 53.4K  
Outlook 2010 Classification and Attribution of emails.

Introduction

This article provides the blueprint of the various mechanisms needed to create a classification and attribution system in Microsoft Outlook 2010. It also provides examples of some common scenarios.

Background

Email has remained unchanged for a long long time. Sure there are minor labels, categories, rules, conversations, folders, attachments, and such but nothing truly earth shattering. Self Description of the email is what's missing.

I work at a large manufacturing company in IT, we have very rigorous processes defined for execution of projects and programs. There are articles/documents for defining what to capture in the software development lifecycle and/or business processes. There are systems in place for capturing that information. There are reports in place for aggregating and displaying results of all this. However, the thing that I continue to see, is that the most valuable information will still show up in Email. The content may eventually make its way to one of the entrenched systems, but when I'm looking for something, I hit the email archive first. And from what I can tell, so does everyone else.

So enter Microsoft Outlook. One of the most feature rich email clients to date. However, emails are still very flat, you might place messages in a specific folder, apply a category/label to it.. But it stops there. Question, what if I have an email that really belongs in more than one folder? Multiple labels? What if I want to use this email as reference data for a project?

How? Can these things be accomplished? Simple.

Classification and Attribution. Creating taxonomy, relationship, and security model that allows email to finally able to meet that goal.

So, playing around with Outlook as a starting point, maybe not getting to my full vision of replacing social media just yet of taking my Tagging Engine to the Cloud.. But let's start small.. if I want attribution and classification on an existing tool, like Outlook.. as that's what I use daily, I started doing proof of concepts to see how this might work.

What I found is Outlook is designed with the DNA specifically geared towards doing what I want to do. I hope Microsoft reads this article, as they have everything ready to go today, they just need some fine tuning to make it all work.

Using the code

My example starts with the Microsoft Ribbon example. This allows you to easily add a right click event to email items. Please download and start with this ribbon example from Microsoft: http://msdn.microsoft.com/en-us/library/ee692172.aspx

Modify the explorer.xml file as follows to enable the right click event. You can add as many buttons as you want.

XML
<contextMenu idMso="ContextMenuMailItem">
        <button id="MyContextMenuMailItem4"
           label="TagIT" 
           onAction="TagEmailItem"/>
   </contextMenu> 
</contextMenus>

And as for the admin screen button (jumping ahead, more on that later). You could do something like this:

XML
<ribbon>
    <tabs>
      <tab id="MyTab"
           getVisible="MyTab_GetVisible"
           label="TagIT">
        <group label="Core" id="MyGroup1" >

       <button id="MyButton2"
        size="large"
        label="TagIT!"
        imageMso="Piggy"
        screentip="Add Tags to your message"
        supertip="Written by Scott Traube"
        onAction="LaunchTagging"/>

       <button id="MyButton"
        size="large"
        label="Find your TagIT Emails"
        imageMso="FindText"
        screentip="Find Emails based on custom tags"
        onAction="LaunchSearch"/>
        </group>
        <group label="Admin" id="MyGroup2" >
          <button id="MyButton3"
        size="large"
        label="Tag Administration"
        imageMso="MagicEightBall"
        screentip="Modify Class Tags and Attributes"
        onAction="LaunchAdmin"/>
        </group>
      </tab>
    </tabs>
</ribbon> 

Step 1. How do I tag (my proof of concept application is called TAG-IT by the way) emails? Enter MSDN and Google (and Bing of course). I first though, well, just append XML at the bottom of each email, as I was coming into this with zero Outlook Add-In exposure . So, XML appended to the end, you can do it, its simple enough, you get a handle on the message in question, append to the body and save it.. Done. But when you do that, what if your email is DOC or HTML type, wow that really screws up the formatting! It can be done though. But maybe this isn't the right approach. As I peeled back the onion a bit more, I discovered that each email item stored in Outlook has user defined attributes. Ahh ha! What I came to discover after some quick tests, its perfect for what I'm trying to accomplish, it's lightweight, leaves a very small footprint, is searchable, it out of the box. Ok, so how do I set a User Defined Attribute? You can start by creating a function defined in the XML above, like:

XML
<button id="MyContextMenuMailItem4" label="TagHello" onAction="helloworld"/>

Now a right click on an email fires this:

C#
public void helloworld(Office.IRibbonControl control)
{
    if (control.Context is Outlook.Selection)
    {
        Outlook.Selection selection =
            control.Context as Outlook.Selection;
        if (selection.Count == 1)
        {
            if (selection[1] is Outlook.MailItem)
            {
                Outlook.MailItem oMailItem =  selection[1] as Outlook.MailItem;
                oMailItem.UserProperties.Add("TagITUpdated",
                Outlook.OlUserPropertyType.olDateTime, false,
                Outlook.OlUserPropertyType.olDateTime);
               
                oMailItem.UserProperties["TagITUpdated"].Value = DateTime.Now;
                    
                oMailItem.UserProperties.Add("MyCustomValue",
                Outlook.OlUserPropertyType.olText, true, 
                Outlook.OlUserPropertyType.olText); 
                oMailItem.UserProperties["MyCustomValue"].Value =  "Hello World";
                oMailItem.Save();
            }
        }
    }
}

That's it. So I've essentially just added the hidden attribute called MyCustomValue to this specific email and set the value at Hello World. I've also done a datestamp on the TagITUpdated attribute.

If I wanted to see the values I've set on an email, I could create another button that's function calls something like this:

C#
public void ShowUserProperties(Outlook.MailItem mail) 
{ 
    Outlook.UserProperties mailUserProperties = null; 
    Outlook.UserProperty mailUserProperty = null; 
    StringBuilder builder = new StringBuilder(); 
    mailUserProperties = mail.UserProperties; 
    try 
    { 
        for (int i = 1; i <= mailUserProperties.Count; i++) 
        { 
            mailUserProperty = mailUserProperties[i]; 
            if (mailUserProperty != null) 
            {
                builder.AppendFormat("Tag: {0} \tValue: {1} \n\r"  mailUserProperty.Name, 
                                     mailUserProperty.Value);
                Marshal.ReleaseComObject(mailUserProperty); 
                mailUserProperty = null; 
            } 
        }
        if (builder.Length > 0)
        {
          System.Windows.Forms.MessageBox.Show(builder.ToString(),
                 "Hidden Property Values");
        }
        else
        {
            System.Windows.Forms.MessageBox.Show("No Hidden Property values found!", 
                                                 "Notification");
        }
    } 
    catch (Exception ex) 
    { 
        System.Windows.Forms.MessageBox.Show(ex.Message); 
    } 
    finally 
    { 
        if (mailUserProperties != null) 
            Marshal.ReleaseComObject(mailUserProperties); 
    } 
}

Step 2. So if I want to have custom classification and attribution for tagging emails. I need to store that reference data somewhere, right? Hmm, maybe a local Access database, MySQL, XML, flat file? All feasible, but just a lot of overhead.. And what if I use more than one computer that's accessing the same exchange server, if I have a local XML file, it wouldn't be available to this other client without some fancy footwork. So finally, I dug a bit, and found the storage item! Bingo! Again, Microsoft apparently designed that specifically with me in mind! I can use a hidden storage item to store each class, attribute types, attribute names, pre-defined attribute criteria.. all that, in these storage hidden storage items, that reside on exchange! To do it properly, I would take an XML object, serialize it to a string, and pop it into the Body of a StorageItem. Great! So this is how you do that:

C#
private void AddtoStorage(string storageIdentifier, string storageContent)
{
    if (!String.IsNullOrEmpty(storageIdentifier) && 
              (!String.IsNullOrEmpty(storageContent))
    {
        Outlook.MAPIFolder folder = 
           Globals.ThisAddIn.Application.GetNamespace(
           "MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
        Outlook.StorageItem storageitem = folder.GetStorage(
           storageIdentifier, Outlook.OlStorageIdentifierType.olIdentifyBySubject);
        storageitem.Body = storageContent;
        storageitem.Save();
    }
}
 
private string GetfromStorage(string storageIdentifier)
{
    if (!String.IsNullOrEmpty(storageIdentifier))
    {
        Outlook.MAPIFolder folder = Globals.ThisAddIn.Application.GetNamespace(
          "MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
        Outlook.StorageItem storageitem = folder.GetStorage(
          storageIdentifier, Outlook.OlStorageIdentifierType.olIdentifyBySubject);
        try
        {
            string bodycontent = storageitem.Body.ToString(); 
            return bodycontent;
        }
        catch (Exception e)
        {
            return "";
        }
    }
    else
    {
        return "";
    }
}

Step 3. Now I have a way of storing the definition of my classification, and I also have a way of storing an attribute on an individual email. But how do I tie them together. Easy, you create a few windows forms in your add-in project! I have 3.

  1. An administration form, for creating, modifying your classification. This is launched from the top ribbon.   
  2. A form you can TAG your individual emails (userproperties) based on classes and attributes created with your admin form. This is launched when you right click an email.
  3. A search form to give you click once attribution from classes and attributes created with your admin form. This is launched from the top ribbon.  

Admin example: 

Image 1

Performing the actual Tagging:

Image 2 

Performing the search:

Image 3 

 

The full implementation of doing something like that gets a little complex. So I'll leave an example of launching a windows form from a right click event. What I'm doing is getting the StoreID (PST, exchange, wherever this email current resides) and the EntryID which is the unique identifier at the time it is in this particular storage location. The two together is the primary key to that email.

C#
public void LaunchTagging(Office.IRibbonControl control)
{
    if (control.Context is Outlook.Selection)
    {
        Outlook.Selection selection = control.Context as Outlook.Selection;
        if (selection.Count == 1)
        {
            OutlookItem olItem = new OutlookItem(selection[1]);
             Form1 formMain = new Form1();
            formMain.StorageID = olItem.Parent.StoreID;
            formMain.EntryID = olItem.EntryID;
            formMain.ShowDialog();
        }
    }
}

The above just gets the ID's and set's them in this form you're going to open, then it launches that form.

Note I have this in the form (ok, you may not need this example, but here it is):

C#
private string entryID = "N/A";
private string storageID = "N/A";  
public string EntryID
{
    get
    {
        return entryID;
    }
    set
    {
        entryID = value;
    }
}

public string StorageID
{
    get
    {
        return storageID;
    }
    set
    {
        storageID = value;
    }
}

So when the form has launched, I can open that specific email up and apply the classification.

This is how I'd open it up based on the storageID and entryID:

C#
Outlook._Application olApp = new Outlook.ApplicationClass();
Outlook._NameSpace olNS = olApp.GetNamespace("MAPI");
Outlook.MAPIFolder oFolder = olNS.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
Outlook._MailItem oMailItem = (Outlook._MailItem)olNS.GetItemFromID(entryID, storageID);

So oMailItem is the object I'm going to add attribution to.

And this might be a way you're using something selected on the screen to set the userproperties:

C#
 // look at the first field
int llindex = listLeft.SelectedIndex;
if (!String.IsNullOrEmpty(lblLeft.Text.ToString() ) && llindex != -1)
{
//save left                   
oMailItem.UserProperties.Add(lblLeft.Text,
Outlook.OlUserPropertyType.olText, true, Outlook.OlUserPropertyType.olText);           
oMailItem.UserProperties[lblLeft.Text].Value = listLeft.SelectedItem.ToString();
}
// look at the cneter field
int lcindex = listCenter.SelectedIndex;
if (!String.IsNullOrEmpty(lblCenter.Text.ToString()) && lcindex != -1)
{
//save center                   
oMailItem.UserProperties.Add(lblCenter.Text, 
  Outlook.OlUserPropertyType.olText, true, Outlook.OlUserPropertyType.olText);                   
oMailItem.UserProperties[lblCenter.Text].Value = listCenter.SelectedItem.ToString();
}
oMailItem.Save();   

Example. I have project called XTR with Envisioning, Planning, Development, and Sustaining phases. I also have different types of requirements such as Business, System, Test, Support. I may also have two other projects going on at the same time called XT and STX but have the same requirement types and same phases.. So in my admin screen, I'd want to create a new project class. It would have three attributes, ProjectName, ProjectPhase, and RequirementType. Further, RequirementType would have 4 list values. I save all this information into the storageitem as a serialized XML object ideally.

Now, I have an email I just received that is for the XTR project, we're in Envisioning phase and the email is from my development team. After further investigation, it appears this email has some great information that I'll need later when I'm writing System Requirement Specifications. So I right click the email, it brings up TagIT windows form. The form pulls the possible classes from the storageitem, I select the project class with one click, the form now shows me the underlying attributes for that class, I select ProjectPhase of Envisioning, and RequirementType of System and then click TAGIT/SAVE. 4 quick clicks and this email now is far more usable.

Code wise, If it were hardcoded, it would look something like this:

C#
oMailItem.UserProperties.Add("ProjectName", 
  Outlook.OlUserPropertyType.olText, true, Outlook.OlUserPropertyType.olText); 
oMailItem.UserProperties["ProjectName"].Value = "XTR";                   
oMailItem.UserProperties.Add("ProjectPhase", 
  Outlook.OlUserPropertyType.olText, true, Outlook.OlUserPropertyType.olText);
oMailItem.UserProperties["ProjectPhase"].Value = "Envisioning"; 
oMailItem.UserProperties.Add("RequirementType", 
  Outlook.OlUserPropertyType.olText, true, Outlook.OlUserPropertyType.olText);
oMailItem.UserProperties["RequirementType"].Value = "System";
oMailItem.Save();    

Be careful, there are some reserved fields in outlook, so if you try to add a UserProperty like Priority, Body, Subject, etc, that already exists, and you'll get an error. So what I do is validate in the admin form to ensure it doesn't exist before saving. So back on topic, At a later point, if I wanted to see all System level requirement emails for the XTR project, I have all the information I need, I'd just need a search screen to query based on XTR, Envisioning, and System.. Now, how do I find it? Microsoft has that covered too.. Its out of the box.

I can bring up the advanced search screen. Goto Fields, select Advanced Tab. Click Field, then User Defined Fields. You should actually find your field named ProjectPhase and RequirementType if you're in the right search context on the correct folder you just added this to. If you were to search on Envisioning, your result is that email you just tagged.

Advanced Find looks like this:

Image 4 

So on the searchability of that attribute. Note that for oMailItem.UserProperties.Add, the third item is a bool. This is for addtofolderfields. Which means, for the folder you're currently residing in, it will add that attribute, no any value for it, just the attribute name. This makes it searchable in that directory. If you already added that search field before to that folder, it doesn't duplicate it, so call that as often as you'd like.

Going to the advanced search each time isn't realistic. And you don't want to have to manually select them each time, especially if you have a list pre-defined and available in your hidden storage item. So how to get the search to show up on the main outlook explorer. First, if you go to the search screen and do a search as follows: [RequirementType]:=('System') [ProjectPhase]:=('Envisioning')

The main search window will provide the results for that call. But what if we want our application to build that search string and do the call for us.. But still end up on the main outlook explorer screen for us? We can, just like this from our custom search windows form:

C#
Outlook.Explorer explorer = Globals.ThisAddIn.Application.ActiveExplorer();
string searchFor = "[RequirementType]:=('System') [ProjectPhase]:=('Envisioning')";
explorer.CurrentFolder = inbox;
explorer.Display();
explorer.Search(searchFor.Trim(), Outlook.OlSearchScope.olSearchScopeSubfolders); 

So your search screen would have to programmatically set the searchFor string based on the criteria pre-populated in your hidden storage item. Just to reiterate, we're never updating that hidden storage item, we ONLY set that from the admin screen.. Then the information is static and just referenced as needed from either when you do the tagging or when you perform searches.

Image 5 

Results:

Image 6 

Now, back to this addtofolderfields. Note that it only added it at the level of the folder that email resided in at the time you set the userattributes. So if you move that email to another folder, or a PST, the attributes remain, but the searchability goes away unless they already exist on that folder. So until you add that attribute to the folder to search on, it won't be found. That is a pain, but the solution is simple, you loop thru all available folders and programmatically set the attributes, so all are searchable.. How do you do that you may ask? This is how:

The store object could represent your default outlook inbox or a PST.

C#
Outlook.MAPIFolder folder = store.GetRootFolder() as Outlook.Folder;
SetFolderAttributes(folder);   

And the SetFolderAttributes function basically loops thru all child folders and does this:

C#
try
{               
subfolder.UserDefinedProperties.Add(AttributeName,
Outlook.OlUserPropertyType.olText, Type.Missing, Type.Missing);
}   

And this is how you'd loop through folders:

C#
if (folder.Folders.Count > 0)
{
foreach (Outlook.Folder subfolder in folder.Folders)
{
// set the class attributes for folder for each attribute for all classes
    for (int i = 0; i < TagAttributes.Count; i++)
        {
        th.SetSubfolderAttributes(subfolder, TagAttributes[i].ToString());
        }  
}
}   

Once you've done this, all your folders are searchable. And when you search on the hidden attribute, it will find it.

More Example Classes the user could create.
HumanResources (class)
    HRTAG (Attribute name)
        "Year End Feedback"
        "Mid Year Feedback"
        "PBA Vacation Related"
    HRPerson (Attribute name)
        "EmployeeX"
        "EmployeeY"
        "EmployeeZ"
Assuming I'm a manager, when I get feedback form business partners that is negative 
or positive and reflective of an individual contributor's performance, I can Tag it with 
that employee's name and year end feedback. At the end of the year, 
I can search on those two attributes and have the information I need.
Or perhaps something like this:
CustomerResponse (Class)
    CustomerType (Attribute Name)
        "Relationship"
        "Federal"
        "Education"
        "Small Business"
        "Enterprise" 
    ResponseType
        "shipping question"
        "Product Praise"
        "Product Defect"
        "Order Related"
    ResponsePriority
        "High"
        "Medium"
        "Low"  

Hope you enjoy this and it spurs some ideas.

Points of Interest

Other ideas of where this can and should go.

  • Classifications set at a corporate or departmental level. So they are standardized. What you'd have to do is determine a location for that, perhaps a SharePoint list, or the cloud.. then from the client perspective you'd read from the master location and persisted locally on a storageitem.
  • Ability to classify an email as Project Viewable or Departmental Viewable, meaning, once it is classified by the user that way, its no longer restricted to just that local user or the users on the TO/CC/BCC, it would aggregate at a higher level, into the cloud, still authorized within the confines of how it's classed, but an email has now become something greater, maybe I'm a follower of a departmental level list, so once someone classifies it that way, I see it there, everyone else with access to that department sees it too… aka Twitter, Google+ type thinking, right? But on emails, its a lot easier on the brain.. See where I'm going with this?
  • TagIT engine in the cloud. Tag news stories, Tag emails, Tag tweets (well they have hashtags already)... basically, tag anything and everything. Now start trending the tags and users with them.. Maybe I want to read news stories tagged similar to how I tagged em.. similar interests. Aka the easiest way to visualize is a Netflix rating system.. And the result? my news isn't politics politics politics.. Its tailored (cycling, triathlon, mountain biking, techy news). This is opposite of how things work today, the ad engines aggregate where you go, what you see, why not just ASK the user!!? (that also goes with my user managed reverse cookie concept, but that's another article). Either way, the quality of all the things behind the scenes could be far greater quality for the user.. TagIT

History

  • Version 1: Posted 3/19/2012.

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0