Thursday, February 12, 2009

Adding a Public Contact Folder Search to the OWA 2007 Address book

Public folders are a great thing they increase the functionality of an Exchange server and while they can cause some support issues it’s great to see they will still be included in the next version of Exchange (it would be great to see them embraced as a feature instead of a burden as they provide some unique functionality you don’t get on other mail systems). One thing that Public folders can be used for is shared address lists with Outlook supporting the ability to add a Public Folder as an address list location in the Outlook Address Book. OWA has never provided this same functionality but for 2003 I came up with a small unsupported (hack) customization to add a public folder search option to the find address function see this post. On 2007 OWA was rewritten and WebDAV is no longer used by any of the OWA code but using the same underlying methods a similar public folder search option can be made available in 2007. Like before this involves modifying some of the OWA files which is not supported due to the nature of these files changing when Service packs and rollup/hotfixes are installed. Although you can change the template forms in 2007 there is still no way of controlling the way things get rendered so this method still uses a HTML injection via the document model. There are several places this code could be placed in 2003 I used the script files which would still work in 2007 but if you have looked at the script files in 2007 you will see a lot more code has been crammed into them and they seem to have gone on a diet in terms of the verbosity of naming standards for variables and whitespace I guess this was done to improve performance. So I chose to modify the addressbook.aspx template form which makes this mod a little easier to service.

How does it work?

The mod involves placing a script block of code just before the closing body tag the reason for positioning it down here is that we want this code to run after the page elements have been rendered to aid with this the defer tag is also used which should defer running of the script until the body has loaded. This could have also been done by using the Onload event in the body but this is already used by the other script files so overloading it in the template breaks other things and I wanted to avoid modifying the scripts themselves. I found the method I described above in my limited testing works okay. What this script block does is gets an array of Divs in the body and then searches through them for a div with an id of divEnts. The first div it finds will represent the upper Address Book part of this page so we don’t want to modify this. The second div it finds with this Id should represent the Contacts section which is where we want to inject the html. So what this script does is inject another entry into this list by using the Innerhtml property of the existing divEnts div. The Div that is injected contains the OWA public folderID of the public folder you want to search as well as the displayName of what you want it to appear as. These two parts need to be customized by you if you wish to use this method. To find the OWAID of the public folder you could use a tool like fiddler to look at protocol captures of OWA or I have another method that uses the EWS convertID operation to convert the Hex entry ID which can be easily obtained using the standard EMS Get-PublicFolder cmdlet.

Finding the OWAID for the Public Folder

Using the standard EMS cmdlet Get-PublicFolder you get access the EntryID of the public folder in its Hex form eg

$pfPublicFolderPath = "\Public Contacts"
$pfFolder = get-publicFolder -identity $pfPublicFolderPath

This would get the Public Folder called Public Contacts in the root and then you can access the EntryID from the EntryID property eg $pfFolder.EntryID. Once you have this EntryID you then need to convert it to the OWAID so it can be used in OWA to do this you need to use the Exchange Web Service convertid operation. I’ve added the necessary EWS code to my EWSUtil powershell library to do this so the code you need just to do the conversion looks like.

[void][Reflection.Assembly]::LoadFile("C:\temp\EWSUtil.dll")
$null = [Reflection.Assembly]::LoadWithPartialName("System.Web")
$mbMailboxEmail = "user@domain.com”
$ewc = new-object EWSUtil.EWSConnection($mbMailboxEmail,$false, "", "", "","")
$Oulookid = $ewc.convertHexidPublicFolder($pfFolder.Entryid,[EWSUtil.EWS.IdFormatType]::OWAid)
[System.Web.HttpUtility]::UrlDecode($Oulookid)

You need to add the email address of the logged on user to the varible $mbMailboxEmail.When you run this code you should see an ID like

PSF.LgAAAAAaRHOQqmYRzZvIAKoAL8RaAwACQRpk/O1eTKnFP2PCsr25AAXCkNnpAAAB

Outputted to the commandline you need to copy this ID for use in the OWA addressbook form.

Modifying the Addressbook.aspx file

Before making any changes to the addressbook.aspx file make sure you copy this file to another location so you have a backup of this file incase the modification doesn’t work and you need to roll back the changes.

On the CAS server locate the OWA forms directory and then locate the addressbook.aspx file in the premium directory see



Open this file in Notepad.
Down the bottom of the file the last three entries in this file should look something like

<% RenderEndOfFileDiv(); %>
</body>
</html>

What we are going to do is put some script in just before the end body tag so the modified end of file should look like

<% RenderEndOfFileDiv(); %>

<script type="text/javascript" language="JavaScript" defer>
var dc=0
var divCollection = document.getElementsByTagName("div");
for (var i=0; i<divCollection.length; i++) {
if(divCollection[i].getAttribute("id") == "divEnts") {
if(dc == 1){
var pfId = "PSF.LgAAAAAaRHOQqmYRzZvIAKoAL8RaAwACQRpk/O1eTKnFP2PCsr25AAXCkNnpAAAB"
var pfName = "Company Contacts"
var newContactEntry = "<div class=snlEntW><div id=divEnt class=\"snlEnt snlDef\" _onclick=onClkCntFld() _fid=\"" + pfId +"\" type=\"IPF.Contact\"><img src=\"current/themes/base/cntctsmll.gif\"><span id=spn>" + pfName + "</span></div></div>";
divCollection[i].innerHTML = divCollection[i].innerHTML + newContactEntry
}
dc++;
}
}
</script>

</body>
</html>


The two varibles

var pfId = "PSF.LgAAAAAaRHOQqmYRzZvIAKoAL8RaAwACQRpk/O1eTKnFP2PCsr25AAXCkNnpAAAB"
var pfName = "Company Contacts"


need to be set to the OWAID you retrieved before and the name of the folder you want.

Thats it the change should be pretty much live as soon as you commit the changes . You need to consider this an untested and unsupported method and only for your own experimentation and testing under lab conditions (make sure your lab has SP1 installed or this wont work). I've put a download of the code to retrieve the OWA public folder ID and the other script change here. For the OWAid code you will need the latest copy of my EWSUtil powershell library