views:

549

answers:

2

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;
    }

    }
}
+1  A: 

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.

Tomas Voracek
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.
NinjaBomb
WebDAV are deprecated and not included in Exchange 2010. (see http://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.
Oleg
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.
NinjaBomb
@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 http://meta.stackoverflow.com/questions/43019/how-do-comment-replies-work).
Oleg
Attempting to get WebDAV working with Exchange 2007 has lead me break things on my desk.
NinjaBomb
+1  A: 

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 http://stackoverflow.com/questions/2275330/create-new-calendar-system-support-in-exchange-owa/2869084#2869084. 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.

Oleg
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?
NinjaBomb
@NinjaBomb. It's a strange problem. Mostly .NET 2.0 till 3.5 is the same and one can upgrade from 2.0 to 3.5 without lot of problems. But it is in theory. One way would be use WebService proxy (add web service reference) like you do and implement the same as with EWS Managed API 1.0. It should work. How I understand Microsoft recommend to use EWS Managed API 1.0 mostly archive more compatibility with different versions of EWS. For example The proxy code from EWS from Exchange 2007 SP1 could be other as from SP2 and SP3 and other as from Exchange 2010. Nevertheless the proxy should work.
Oleg
@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 http://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.
Oleg
@NinjaBomb: In the way looking on http://msdn.microsoft.com/en-us/library/bb402172(v=EXCHG.140).aspx and http://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 http://msdn.microsoft.com/en-us/library/exchangewebservices(v=EXCHG.140).aspx) and implement so your program. See also Exchange Web Services Operations (url: http://msdn.microsoft.com/en-us/library/bb409286(v=EXCHG.140).aspx)
Oleg
@Oleg 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.
NinjaBomb
@Oleg 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!
NinjaBomb
@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.
Oleg
@NinjaBomb: It sounds wonderfull!
Oleg
@Oleg - added my code solution to the question.
NinjaBomb