Overview
The article below explains how to create an Outlook vCard on the fly from a website. The example in the article uses some static data as it's easier to explain, but it is simple to make it more dynamic.
History
I've been doing a little work on our company's intranet system. Interestingly, the company doesn't have a global contacts solution, so each individual group company has implemented their own. Some of these solutions consist of having an Excel spreadsheet with a list of names and numbers on. My little bit of work was to create an intranet based contacts solution to replace one of these Excel based systems. The other interesting thing about this company is that they don't use Exchange, the French owners having decided that everything Microsoft is bad. This has obviously been ignored at a desktop level, so instead of using Netscape Communicator, everyone uses Outlook. The sad fact is without Exchange or even a group-wide Active Directory solution, the contact information is missing. The only way to hold employees contact details is to create your own contacts list in Outlook.
The Solution
To make people's lives easier, I decided that it would be helpful if they can export the contact details they have just found to the Outlook vCard format, which would then allow them to save it to their contacts list. Having decided that I was going to do that, I had to think about how best to get the desired response. I didn't want the site to have to create a local copy of the vCard for the client to download. I decided that changing the response stream of a page would be best. The vCard could then be generated on the fly. So below is how I achieved this solution.
vCard
The first thing I had to do is work out the format that vCards are held in. Luckily, this was as simple as opening it in Notepad.
The format looks pretty much like this:
BEGIN:VCARD
VERSION:2.1
N:Davey;Chris;J S;Mr
URL;WORK:www.chemlock.co.uk
END:VCARD
You will notice that basically the field identifiers are split from the data with a colon ":", and any identifiers or data that is split into parts is split using the semi-colon ";". Apart from that, it's pretty much plain text. This obviously made me think the task would be fairly simple.
Creating a new page
I decided previously that I wanted to generate the vCard on the fly rather than have the server save a file somewhere. To do this, I would need to change the output stream of the page HTTP response. I didn't want to do this on the original page as it would obviously trash my existing page data, so I needed a way of creating a new page and having that page's HTTP response stream changed. To do this, I needed to revert back to good old JavaScript. I have a DataGrid
ItemCommand
event that calls the following:
private void bdgResults_ItemCommand(object source,DataGridCommandEventArgs e)
{
switch(e.CommandName)
{
case "vCardExport":
Response.Write(@"<script language = "'Javascript'">var" +
@" win=window.open('vCard.aspx',null,'width=50,height=50," +
@"top=100,left=100','true');</script>");
break;
}
}
All this code does is create a new window with whatever content vCard.aspx holds. So now, we need to create vCard.aspx.
vCard.aspx HTML
For the HTML, I didn't change a thing. See, there is nothing there:.
<%@ Page language="c#" Codebehind="vCard.aspx.cs"
AutoEventWireup="false" Inherits="TISContacts.vCard" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>vCard</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio .NET
7.1">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name=vs_defaultClientScript content="JavaScript">
<meta name=vs_targetSchema content="http://schemas.microsoft.com/intellisense/ie5">
</head>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
</form>
</body>
</html>
vCard.aspx code-behind
OK, here is where the magic occurs. In this code, we need to clear the HTTP response stream and then write our own. Here is the full code. I'll explain after you've had a look at it.
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
namespace TISContacts
{
public class vCard : System.Web.UI.Page
{
private void Page_Load(object sender, System.EventArgs e)
{
cmpVCard.VCard(Response);
}
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
InitializeComponent();
base.OnInit(e);
}
private void InitializeComponent()
{
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
}
public class cmpVCard : System.ComponentModel.Component
{
#region Attributes
private const string nameFirst = "Chris";
private const string nameLast = "Davey";
private const string nameMiddle = "J S";
private const string nameTitle = "Mr";
private const string email = "chris.davey@chemlock.co.uk";
private const string uRL = "www.chemlock.co.uk";
private const string telephone = "(0) 1555 555555";
private const string fax = "(0) 1555 555555";
private const string mobile = "(0) 7555 555555";
private const string company = "Chemlock";
private const string department = "Owner";
private const string title = "Managing Director";
private const string profession = "Developer";
private const string office = "Bedroom";
private const string addressTitle = "Chemlock";
private const string streetName = "25 Nowhere Str";
private const string city = "London";
private const string region = "Surrey";
private const string postCode = "GU30 7BZ";
private const string country = "ENGLAND";
#endregion Attributes
#region Component Designer generated code
public cmpVCard(System.ComponentModel.IContainer Container)
{
Container.Add(this);
}
public cmpVCard()
{
InitializeComponent();
}
protected override void Dispose(Boolean disposing)
{
if (disposing)
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
private System.ComponentModel.IContainer components;
[System.Diagnostics.DebuggerStepThrough()]
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
public static void VCard(HttpResponse response)
{
response.Clear();
response.Charset = "";
response.ContentType = "text/x-vCard";
System.IO.StringWriter stringWrite = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter htmlWrite = new HtmlTextWriter(stringWrite);
stringWrite.WriteLine("BEGIN:VCARD");
stringWrite.WriteLine("VERSION:2.1");
stringWrite.WriteLine("N:" + nameLast + ";" + nameFirst +
";" + nameMiddle + ";" + nameTitle);
stringWrite.WriteLine("FN:" + nameFirst + " " +
nameMiddle + " " + nameLast);
stringWrite.WriteLine("ORG:" + company + ";" + department);
stringWrite.WriteLine("URL;WORK:" + uRL);
stringWrite.WriteLine("TITLE:" + title);
stringWrite.WriteLine("ROLE:" + profession);
stringWrite.WriteLine("TEL;WORK;VOICE:" + telephone);
stringWrite.WriteLine("TEL;WORK;FAX:" + fax);
stringWrite.WriteLine("TEL;CELL;VOICE:" + mobile);
stringWrite.WriteLine("EMAIL;PREF;INTERNET:" + email);
stringWrite.WriteLine("ADR;WORK;ENCODING=QUOTED-PRINTABLE:" + ";" +
office + ";" + addressTitle + "=0D" +
streetName + ";" + city + ";" +
region + ";" + postCode + ";" + country);
stringWrite.WriteLine("END:VCARD");
response.Write(stringWrite.ToString());
response.End();
}
}
}
To start with, it's all fairly simple. Our vCard Page_Load
instantiates a component that we've called cmpVCard
. It's this component that strips out the response stream and then writes our new stream. The meat of the thing is in a component so that it could be exported into a separate file and used through-out the application. At present, I've put the code in vCard.aspx.cs so you can see what it does. It's not that confusing. You can pretty much copy the "#region Component Designer generated code
" as is into your code. The part that does the fancy stuff is the vCard
method. This, as you can see, first kills anything in the response stream.
response.Clear();
response.Charset = "";
Then, it changes the response type to be in the vCard MIME format.
response.ContentType = "text/x-vCard";
The rest is just building up the StringWriter
with our desired output and then writing it to the response. Simple as that. With Internet Explorer, this simply opens the vCard. In something like Firefox, you get prompted for a program to open the file with. This, by default, should be set to Microsoft Outlook.
Obviously, I've used some const
strings to put my results in, but in the real world solution, a Data Transfer Object is passed in via a Session variable that is set on the previous page. I'm sure you can see how this can be made dynamic. If you have any questions, please post them below or on my blog.
Job done. www.chemlock.co.uk