Skip to main content

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

Popular posts from this blog

The MailboxConcurrency limit and using Batching in the Microsoft Graph API

If your getting an error such as Application is over its MailboxConcurrency limit while using the Microsoft Graph API this post may help you understand why. Background   The Mailbox  concurrency limit when your using the Graph API is 4 as per https://docs.microsoft.com/en-us/graph/throttling#outlook-service-limits . This is evaluated for each app ID and mailbox combination so this means you can have different apps running under the same credentials and the poor behavior of one won't cause the other to be throttled. If you compared that to EWS you could have up to 27 concurrent connections but they are shared across all apps on a first come first served basis. Batching Batching in the Graph API is a way of combining multiple requests into a single HTTP request. Batching in the Exchange Mail API's EWS and MAPI has been around for a long time and its common, for email Apps to process large numbers of smaller items for a variety of reasons.  Batching in the Graph is limited to a m

Sending a Message in Exchange Online via REST from an Arduino MKR1000

This is part 2 of my MKR1000 article, in this previous post  I looked at sending a Message via EWS using Basic Authentication.  In this Post I'll look at using the new Outlook REST API  which requires using OAuth authentication to get an Access Token. The prerequisites for this sketch are the same as in the other post with the addition of the ArduinoJson library  https://github.com/bblanchon/ArduinoJson  which is used to parse the Authentication Results to extract the Access Token. Also the SSL certificates for the login.windows.net  and outlook.office365.com need to be uploaded to the devices using the wifi101 Firmware updater. To use Token Authentication you need to register an Application in Azure https://msdn.microsoft.com/en-us/office/office365/howto/add-common-consent-manually  with the Mail.Send permission. The application should be a Native Client app that use the Out of Band Callback urn:ietf:wg:oauth:2.0:oob. You need to authorize it in you tenant (eg build a small ap

How to test SMTP using Opportunistic TLS with Powershell and grab the public certificate a SMTP server is using

Most email services these day employ Opportunistic TLS when trying to send Messages which means that wherever possible the Messages will be encrypted rather then the plain text legacy of SMTP.  This method was defined in RFC 3207 "SMTP Service Extension for Secure SMTP over Transport Layer Security" and  there's a quite a good explanation of Opportunistic TLS on Wikipedia  https://en.wikipedia.org/wiki/Opportunistic_TLS .  This is used for both Server to Server (eg MTA to MTA) and Client to server (Eg a Message client like Outlook which acts as a MSA) the later being generally Authenticated. Basically it allows you to have a normal plain text SMTP conversation that is then upgraded to TLS using the STARTTLS verb. Not all servers will support this verb so if its not supported then a message is just sent as Plain text. TLS relies on PKI certificates and the administrative issue s that come around certificate management like expired certificates which is why I wrote th
All sample scripts and source code is provided by for illustrative purposes only. All examples are untested in different environments and therefore, I cannot guarantee or imply reliability, serviceability, or function of these programs.

All code contained herein is provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.