Extract Exchange 2007 Public Calendar Appointments using Exchange Web Services API
Asked Answered
F

2

24

We have a public calendar for our company set up in an Exchange 2007 Public Folder. I am able to retrieve my personal calendar appointments for the current day using the code below. I have searched high and low online and I cannot find one example of someone retrieving calendar information from a Public Folder calendar.

It seems like it should be doable, but I cannot for the life of me get it working. How can I modify the code below to access the calendar? I am not interested in creating any appointments through asp.net, just retrieving a simple list. I am open to any other suggestions as well. Thanks.

ADDED BOUNTY
- I can't be the only person that ever needed to do this. Let's get this problem solved for future generations.

UPDATED AGAIN DUE TO IGNORANCE
- I failed to mention that the project I am working on is .NET 2.0 (very important don't you think?).

* ADDED MY CODE SOLUTION BELOW *
- I have replaced my original code example with the code that ended up working. Many thanks to Oleg for providing the code to find the public folder, which was the hardest part.. I have modified the code using the example from here http://msexchangeteam.com/archive/2009/04/21/451126.aspx to use the simpler FindAppointments method.

This simple example returns an html string with the appointments, but you can use it as a base to customize as needed. You can see our back and forth under his answer below.

using System;
using Microsoft.Exchange.WebServices.Data;
using System.Net;

namespace ExchangePublicFolders
{
    public class Program
    {
        public static FolderId FindPublicFolder(ExchangeService myService, FolderId baseFolderId,
        string folderName)
        {

        FolderView folderView = new FolderView(10, 0);
        folderView.OffsetBasePoint = OffsetBasePoint.Beginning;
        folderView.PropertySet = new PropertySet(FolderSchema.DisplayName, FolderSchema.Id);

        FindFoldersResults folderResults;
        do
        {
            folderResults = myService.FindFolders(baseFolderId, folderView);

            foreach (Folder folder in folderResults)
                if (String.Compare(folder.DisplayName, folderName, StringComparison.OrdinalIgnoreCase) == 0)
                    return folder.Id;

            if (folderResults.NextPageOffset.HasValue)
                folderView.Offset = folderResults.NextPageOffset.Value;
        }
        while (folderResults.MoreAvailable);

        return null;
    }

    public static string MyTest()
    {
        ExchangeService myService = new ExchangeService(ExchangeVersion.Exchange2007_SP1);

        myService.Credentials = new NetworkCredential("USERNAME", "PASSWORD", "DOMAIN");
        myService.Url = new Uri("https://MAILSERVER/ews/exchange.asmx");

        Folder myPublicFoldersRoot = Folder.Bind(myService, WellKnownFolderName.PublicFoldersRoot);
        string myPublicFolderPath = @"PUBLIC_FOLDER_CALENDAR_NAME";
        string[] folderPath = myPublicFolderPath.Split('\\');
        FolderId fId = myPublicFoldersRoot.Id;
        foreach (string subFolderName in folderPath)
        {
            fId = Program.FindPublicFolder(myService, fId, subFolderName);
            if (fId == null)
            {
                return string.Format("ERROR: Can't find public folder {0}", myPublicFolderPath);

            }
        }

        Folder folderFound = Folder.Bind(myService, fId);
        if (String.Compare(folderFound.FolderClass, "IPF.Appointment", StringComparison.Ordinal) != 0)
        {
            return string.Format("ERROR: Public folder {0} is not a Calendar", myPublicFolderPath);

        }

        CalendarFolder AK_Calendar = CalendarFolder.Bind(myService, fId, BasePropertySet.FirstClassProperties);

        FindItemsResults<Appointment> AK_appointments = AK_Calendar.FindAppointments(new CalendarView(DateTime.Now,DateTime.Now.AddDays(1)));


        string rString = string.Empty;


        foreach (Appointment AK_appoint in AK_appointments)
        {

            rString += string.Format("Subject: {0}<br />Date: {1}<br /><br />",  AK_appoint.Subject, AK_appoint.Start);    
        }

        return rString;
    }

    }
}
Forestay answered 2/9, 2010 at 21:42 Comment(5)
Hi! The code FindItemsResults<Appointment> look like nice. It seems to me that you forget to set PropertySet for the CalendarView. It is important. If you will use only Subject and Start properties of appointments from the search results you should set this two properties ItemSchema.Subject and AppointmentSchema.Start. If you don't do this all PropertySet.FirstClassProperties will be send from the Exchange server. I included in folder binding this property set only to show how much there are. Moreover I recommend you continue to use paging.Commanding
By the way the usage of ItemView and FindItemsResults<Item> come because I modified one other my example which enumerated e-mails. In the Mailbox folder you will find not only emails, but also requirements to conform an appointment. So to enumerate the mailbox you should better use basis class Item. It seems that with the calender folder you will not have the problem, but think about this possible problem. Best regards and good luck!Commanding
@Commanding I am going to keep the paging. I took the PropertySet off because I wanted to play around with all of the properties. I am going to add it back once I decide exactly what properties I want to use. I am going to stick to only the calendar for now, but if I need to access an e-mail mailbox I will use the Itemview.Forestay
Fine! I want be sure that you correct understand the advantages and disadvantages of the different changes of code.Commanding
Much belated +1. I think this has put me in the right direction (after figuring out that the setup I have, as is, needs .Net 4.0 FULL - not client, to compile properly).Greasy
C
15

Like promised here is a code example. I used the Microsoft Exchange Web Services (EWS) Managed API 1.0 and recommend you to do the same. The most comments I included in the code

using System;
using Microsoft.Exchange.WebServices.Data;
using System.Net;

namespace ExchangePublicFolders {
    class Program {
        static FolderId FindPublicFolder (ExchangeService myService, FolderId baseFolderId,
            string folderName) {

            // We will search using paging. We will use page size 10
            FolderView folderView = new FolderView (10,0);
            folderView.OffsetBasePoint = OffsetBasePoint.Beginning;
            // we will need only DisplayName and Id of every folder
            // se we'll reduce the property set to the properties
            folderView.PropertySet = new PropertySet (FolderSchema.DisplayName,
                FolderSchema.Id);

            FindFoldersResults folderResults;
            do {
                folderResults = myService.FindFolders (baseFolderId, folderView);

                foreach (Folder folder in folderResults)
                    if (String.Compare (folder.DisplayName, folderName, StringComparison.OrdinalIgnoreCase) == 0)
                        return folder.Id;

                if (folderResults.NextPageOffset.HasValue)
                    // go to the next page
                    folderView.Offset = folderResults.NextPageOffset.Value;
            }
            while (folderResults.MoreAvailable);

            return null;
        }

        static void MyTest () {
            // IMPORTANT: ExchangeService is NOT thread safe, so one should create an instance of
            // ExchangeService whenever one needs it.
            ExchangeService myService = new ExchangeService (ExchangeVersion.Exchange2007_SP1);

            myService.Credentials = new NetworkCredential ("[email protected]", "myPassword00");
            myService.Url = new Uri ("http://mailwebsvc-t.services.local/ews/exchange.asmx");
            // next line is very practical during development phase or for debugging
            myService.TraceEnabled = true;

            Folder myPublicFoldersRoot = Folder.Bind (myService, WellKnownFolderName.PublicFoldersRoot);
            string myPublicFolderPath = @"OK soft GmbH (DE)\Gruppenpostfächer\_Template - Gruppenpostfach\_Template - Kalender";
            string[] folderPath = myPublicFolderPath.Split('\\');
            FolderId fId = myPublicFoldersRoot.Id;
            foreach (string subFolderName in folderPath) {
                fId = FindPublicFolder (myService, fId, subFolderName);
                if (fId == null) {
                    Console.WriteLine ("ERROR: Can't find public folder {0}", myPublicFolderPath);
                    return;
                }
            }

            // verify that we found 
            Folder folderFound = Folder.Bind (myService, fId);
            if (String.Compare (folderFound.FolderClass, "IPF.Appointment", StringComparison.Ordinal) != 0) {
                Console.WriteLine ("ERROR: Public folder {0} is not a Calendar", myPublicFolderPath);
                return;
            }

            CalendarFolder myPublicFolder = CalendarFolder.Bind (myService,
                //WellKnownFolderName.Calendar,
                fId,
                PropertySet.FirstClassProperties);

            if (myPublicFolder.TotalCount == 0) {
                Console.WriteLine ("Warning: Public folder {0} has no appointment. We try to create one.", myPublicFolderPath);

                Appointment app = new Appointment (myService);
                app.Subject = "Writing a code example";
                app.Start = new DateTime (2010, 9, 9);
                app.End = new DateTime (2010, 9, 10);
                app.RequiredAttendees.Add ("[email protected]");
                app.Culture = "de-DE";
                app.Save (myPublicFolder.Id, SendInvitationsMode.SendToNone);
            }

            // We will search using paging. We will use page size 10
            ItemView viewCalendar = new ItemView (10);
            // we can include all properties which we need in the view
            // If we comment the next line then ALL properties will be
            // read from the server. We can see there in the debug output
            viewCalendar.PropertySet = new PropertySet (ItemSchema.Subject);
            viewCalendar.Offset = 0;
            viewCalendar.OffsetBasePoint = OffsetBasePoint.Beginning;
            viewCalendar.OrderBy.Add (ContactSchema.DateTimeCreated, SortDirection.Descending);

            FindItemsResults<Item> findResultsCalendar;
            do {
                findResultsCalendar = myPublicFolder.FindItems (viewCalendar);

                foreach (Item item in findResultsCalendar) {
                    if (item is Appointment) {
                        Appointment appoint = item as Appointment;
                        Console.WriteLine ("Subject: \"{0}\"", appoint.Subject);
                    }
                }

                if (findResultsCalendar.NextPageOffset.HasValue)
                    // go to the next page
                    viewCalendar.Offset = findResultsCalendar.NextPageOffset.Value;
            }
            while (findResultsCalendar.MoreAvailable);
        }
        static void Main (string[] args) {
            MyTest();
        }
    }
}

You should update the string myPublicFolderPath to the value with your public calender folder. I set myService.TraceEnabled = true which produce long output with debug information. You should of cause remove the line for production.

UPDATED: Some additional links you could find in Create new calendar system support in Exchange OWA. If you not yet seen the videos and you want to use Exchange Web Services I would recommend you to watch there. It could save your time in the future.

Commanding answered 9/9, 2010 at 17:13 Comment(6)
Thanks! I left out a very important piece of information in my question. My project is stuck on .NET 2.0. Sorry about that. Since that's my fault and the solution you have provided is extremely comprehensive and took some time to put together, I'll give you the bounty. Since I cannot add the dll to the project without upgrading it, what would you do? My first thought is to have a totally separate website serving a web service to pull the calendar data, but that sounds like overkill. What do you think?Forestay
@NinjaBomb: If you look at the debug output which produce my test program (myService.TraceEnabled = true) you can see that EWS Managed API 1.0 send the same requests which you can send by WebProxy. For example, look at msdn.microsoft.com/en-us/library/aa493892(v=EXCHG.140).aspx. You can of cause not use FolderQueryTraversalType.Deep for public folders, but you can easy modify the code so that it do the same as FindPublicFolder from my program.Commanding
@NinjaBomb: In the way looking on msdn.microsoft.com/en-us/library/bb402172(v=EXCHG.140).aspx and msdn.microsoft.com/en-us/library/aa563918(v=EXCHG.140).aspx and so on you can find the corresponding code example in EWS as proxy (see msdn.microsoft.com/en-us/library/…) and implement so your program. See also Exchange Web Services Operations (url: msdn.microsoft.com/en-us/library/bb409286(v=EXCHG.140).aspx)Commanding
@Commanding I was able to add a .net 3.5 project to my solution. I can add the reference to my .net 2.0 website. I have both frameworks installed on the server so everything looks like it will work. What is the path you are using for the public folder? I've tried every variation of the path but I keep getting the Can't find public folder error. I am using an administrator account.Forestay
@Commanding GOT IT! Just had to use the calendar name! Now I'll modify it up a little to return just the current day. I'll post my customization of your code in the answer if that's OK with you. Thanks again!Forestay
@NinjaBomb: Very good news! With .NET 3.5 all will be simple. In my example there are public folder of the type Calender with the path @"OK soft GmbH (DE)\Gruppenpostfächer\_Template - Gruppenpostfach\_Template - Kalender" (see variable myPublicFolderPath). What is the path to the calender folder? I strict recommend you first of all make my console program work in your environment. Debug information displayed in the FindPublicFolder will help you. If you will have problem post me the debug output and the full path to the calendar public folder in your environment.Commanding
M
1

I did similar thing for Exchange 2003, but using WebDAV Search method (http://msdn.microsoft.com/en-us/library/aa143053%28v=EXCHG.65%29.aspx).

http://geekswithblogs.net/cskardon/archive/2008/12/01/hunting-those-elusive-public-folders-using-exchange-web-services-part.aspx may help.

Miscall answered 7/9, 2010 at 22:25 Comment(5)
Thanks for the links. I have seen the geekswithblogs.net post about searching public folders in every search I have performed. Unless I totally missed it, I don't see where he actually pulls any calendar appointments from a public folder calendar. I will check out the WebDAV method, but I haven't found any examples of it being used for Exchange 2007 calendars or if it will work.Forestay
WebDAV are deprecated and not included in Exchange 2010. (see msdn.microsoft.com/en-us/library/dd877032.aspx). One do can access Exchange 2007 Public Folder with Exchange Web Services. If I will find a little time I'll post an example later.Commanding
Oleg You post an answer with a good example of pulling appointments from a public folder calendar that works on my side and you get the bounty! I've look through every example that Microsoft provided for Exchange Web Services, but none of them involve public folders. I'll test whatever you post as soon as I can.Forestay
@NinjaBomb: I posted the code which I promised. I can comment different steps if you will have some questions. I wrote the code mostly for demonstration. One more remark. Please write @ before my name if you write a comment to me (see meta.stackexchange.com/questions/43019/…).Commanding
Attempting to get WebDAV working with Exchange 2007 has lead me break things on my desk.Forestay

© 2022 - 2024 — McMap. All rights reserved.