Tuesday, March 27, 2012

EWS Managed API and Powershell How-To Series Part 6 FreeBusy and OOF Operations

In this post I'm going to continue looking at the special operations in EWS that allows you to get and set the unconventional data that is stored in an Exchange mailbox like the FreeBusy information and the Out of Office Message. First off lets look at the FreeBusyTime.
 

Getting Freebusy time

Freebusy information is one of the more useful calendar features that Exchange offers in EWS the ability to access the freebusy information is provided by the GetUserAvailiblity operation. This operation can be useful in a number of scenarios especially when you may want to query a large number of calendars. One example is a freebusy board or meeting room website

When you make a FreeBusy request there are two constraints on the request that are outlined in http://technet.microsoft.com/en-us/library/hh310374.aspx  "By default, Exchange 2007 accepts availability requests for 42 days of free/busy information and Exchange 2010 may request 62 days of free/busy information. If the request exceeds the default 42 limit imposed by Exchange 2007, the request will fail." The other constraint in the number of users you can query at one time is limited to 100 per call.

The detail level of information that is returned by this operation is also controlled by the value of RequestedFreeBusyView that you set. Essentially two different data-sets could be returned by this operation the first is the MergedFreeBusyStatus which are the FreeBusy Slots from a mailbox by default in 30 minutes increments. The second dataset is a detailed array of calendar appointment during the freebusy period if you have requested Calendar Details and you have enough rights on the Calendar to see Subject and Location information (Otherwise you will just get Start and EndTimes).

Lets look at an example of getting the freebusy time with the EWS Managed API for one user is this example we look at the CalendarDetails array to show the information that can be returned.

  1. $StartTime = [DateTime]::Parse([DateTime]::Now.ToString("yyyy-MM-dd 0:00"))  
  2. $EndTime = $StartTime.AddDays(7)  
  3.   
  4. $drDuration = new-object Microsoft.Exchange.WebServices.Data.TimeWindow($StartTime,$EndTime)  
  5. $AvailabilityOptions = new-object Microsoft.Exchange.WebServices.Data.AvailabilityOptions  
  6. $AvailabilityOptions.RequestedFreeBusyView = [Microsoft.Exchange.WebServices.Data.FreeBusyViewType]::DetailedMerged  
  7.   
  8. $listtype = ("System.Collections.Generic.List"+'`'+"1") -as "Type"
  9. $listtype = $listtype.MakeGenericType("Microsoft.Exchange.WebServices.Data.AttendeeInfo" -as "Type")
  10. $Attendeesbatch = [Activator]::CreateInstance($listtype)
  11.   
  12. $Attendee = new-object Microsoft.Exchange.WebServices.Data.AttendeeInfo("user@domain.com")  
  13. $Attendeesbatch.add($Attendee)  
  14.   
  15. $availresponse = $service.GetUserAvailability($Attendeesbatch,$drDuration,[Microsoft.Exchange.WebServices.Data.AvailabilityData]::FreeBusy,$AvailabilityOptions)  
  16. foreach($avail in $availresponse.AttendeesAvailability){  
  17.     foreach($cvtEnt in $avail.CalendarEvents){  
  18.         "Start : " + $cvtEnt.StartTime  
  19.         "End : " + $cvtEnt.EndTime  
  20.         "Subject : " + $cvtEnt.Details.Subject  
  21.         "Location : " + $cvtEnt.Details.Location  
  22.         ""  
  23.     }  
  24. }  
One useful example is to do this for a Group of users so first you will need a Distribution Group and we will then use the ExandGroup EWS operation to get all the members of that group which will give us all the Email address's of the users that we will then get the FreeBusy information for. You also need some code to ensure you can relate the information in the CalendarDetails array to that of the request because by default in this array there is no information relating the recipient this data belongs to. (Not you can actually use the Distribution Groups address instead of doing the expand but if you do this the results do get merged into one Entry which is hard to separate).

  1. $GroupAddress = "testinggroup@domain.com"  
  2. $StartTime = [DateTime]::Parse([DateTime]::Now.ToString("yyyy-MM-dd 0:00"))  
  3. $EndTime = $StartTime.AddDays(7)  
  4.   
  5. $drDuration = new-object Microsoft.Exchange.WebServices.Data.TimeWindow($StartTime,$EndTime)  
  6. $AvailabilityOptions = new-object Microsoft.Exchange.WebServices.Data.AvailabilityOptions  
  7. $AvailabilityOptions.RequestedFreeBusyView = [Microsoft.Exchange.WebServices.Data.FreeBusyViewType]::DetailedMerged  
  8.   
  9. $listtype = ("System.Collections.Generic.List"+'`'+"1") -as "Type"
  10. $listtype = $listtype.MakeGenericType("Microsoft.Exchange.WebServices.Data.AttendeeInfo" -as "Type")
  11. $Attendeesbatch = [Activator]::CreateInstance($listtype)
  12.   
  13. $GroupMembers = $service.ExpandGroup($GroupAddress)  
  14. $adrHash = @{}  
  15. $adcnt = 0  
  16. foreach($Address in $GroupMembers.Members){  
  17.     $Attendee = new-object Microsoft.Exchange.WebServices.Data.AttendeeInfo($Address.Address)  
  18.     $Attendeesbatch.add($Attendee)  
  19.     $adrHash.add($adcnt,$Address.Address)  
  20.     $adcnt++  
  21.     $Address.Address  
  22. }  
  23. $adcnt = 0  
  24. $availresponse = $service.GetUserAvailability($Attendeesbatch,$drDuration,[Microsoft.Exchange.WebServices.Data.AvailabilityData]::FreeBusy,$AvailabilityOptions)  
  25. foreach($avail in $availresponse.AttendeesAvailability){  
  26.       
  27.     foreach($cvtEnt in $avail.CalendarEvents){  
  28.         "Mailbox : " + $adrHash[$adcnt]  
  29.         "Start : " + $cvtEnt.StartTime  
  30.         "End : " + $cvtEnt.EndTime  
  31.         "Subject : " + $cvtEnt.Details.Subject  
  32.         "Location : " + $cvtEnt.Details.Location  
  33.         ""  
  34.     }  
  35.     $adcnt++  
  36. }  
Out of Office Information

EWS provides access to the Out of Office information via GetUserOofSettings and SetUserOofSettings  Operations. Or one other way of displaying the OOF setting for a user which doesn't require rights on a users mailbox is to use MailTips. Using the OOF via GetUserOofSettings/SetUserOofSettings setting is pretty easy lets looks at the basics of Getting and Displaying the OOF settings.

  1. $oofSetting = $service.GetUserOofSettings("user@domain.com")  
  2. "OOF State : " + $oofSetting.State  
  3. "InternalReply : " + $oofSetting.InternalReply  
  4. "ExternalReply : " + $oofSetting.ExternalReply  
  5. "AllowExternalOof : " + $oofSetting.AllowExternalOof  
To set the OOF State and or the Message property you should first get the current setting modify the property you want to modify and then update the OOF.

eg to set the OOF of the currently logged on user you could use the following code.

  1. $MailboxName = "user@domain.com"  
  2. $oofSetting = $service.GetUserOofSettings($MailboxName)  
  3. $oofSetting.State.ToString()  
  4. $oofSetting.InternalReply = new-object Microsoft.Exchange.WebServices.Data.OofReply("Test 123")  
  5. $oofSetting.ExternalReply.ToString()  
  6. $oofSetting.State = [Microsoft.Exchange.WebServices.Data.OofState]::Enabled  
  7. $service.SetUserOofSettings($MailboxName$oofSetting);   

An example of using MailTips to get the OOF needs a little more code because there are no methods in the EWS Managed API to make use of the MailTips operations you need to use some Raw XML instead.

  1. $MailboxName = "user@domain.com"  
  2.   
  3. $expRequest = @"   
  4. <?xml version="1.0" encoding="utf-8"?>   
  5. <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">   
  6. <soap:Header><RequestServerVersion Version="Exchange2010_SP1" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" />   
  7. </soap:Header>   
  8. <soap:Body>   
  9. <GetMailTips xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">   
  10. <SendingAs>   
  11. <EmailAddress xmlns="http://schemas.microsoft.com/exchange/services/2006/types">$MailboxName</EmailAddress>   
  12. </SendingAs>   
  13. <Recipients>   
  14. "@     
  15.     
  16. foreach($mbMailbox in $Mailboxes){     
  17.     $expRequest = $expRequest + "<Mailbox xmlns=`"http://schemas.microsoft.com/exchange/services/2006/types`"><EmailAddress>$mbMailbox</EmailAddress></Mailbox>"      
  18. }     
  19.     
  20. $expRequest = $expRequest + "</Recipients><MailTipsRequested>OutOfOfficeMessage</MailTipsRequested></GetMailTips></soap:Body></soap:Envelope>"    
  21. $mbMailboxFolderURI = New-Object System.Uri($service.url)     
  22. $wrWebRequest = [System.Net.WebRequest]::Create($mbMailboxFolderURI)     
  23. $wrWebRequest.KeepAlive = $false;     
  24. $wrWebRequest.Headers.Set("Pragma""no-cache");     
  25. $wrWebRequest.Headers.Set("Translate""f");     
  26. $wrWebRequest.Headers.Set("Depth""0");     
  27. $wrWebRequest.ContentType = "text/xml";     
  28. $wrWebRequest.ContentLength = $expRequest.Length;     
  29. $wrWebRequest.Timeout = 60000;     
  30. $wrWebRequest.Method = "POST";     
  31. $wrWebRequest.Credentials = $cred    
  32. $bqByteQuery = [System.Text.Encoding]::ASCII.GetBytes($expRequest);     
  33. $wrWebRequest.ContentLength = $bqByteQuery.Length;     
  34. $rsRequestStream = $wrWebRequest.GetRequestStream();     
  35. $rsRequestStream.Write($bqByteQuery, 0, $bqByteQuery.Length);     
  36. $rsRequestStream.Close();     
  37. $wrWebResponse = $wrWebRequest.GetResponse();     
  38. $rsResponseStream = $wrWebResponse.GetResponseStream()     
  39. $sr = new-object System.IO.StreamReader($rsResponseStream);     
  40. $rdResponseDocument = New-Object System.Xml.XmlDocument     
  41. $rdResponseDocument.LoadXml($sr.ReadToEnd());     
  42. $RecipientNodes = @($rdResponseDocument.getElementsByTagName("t:RecipientAddress"))     
  43. $Datanodes = @($rdResponseDocument.getElementsByTagName("t:OutOfOffice"))     
  44. for($ic=0;$ic -lt $RecipientNodes.length;$ic++){     
  45.     if($Datanodes[$ic].ReplyBody.Message -eq ""){     
  46.         $RecipientNodes[$ic].EmailAddress + " : In the Office"    
  47.     }     
  48.     else{     
  49.         $RecipientNodes[$ic].EmailAddress + " : Out of Office"    
  50.     }     
  51. }