locked
How to get appointment if deleted from public calendar. RRS feed

  • Question

  • I have a problem regarding deleting appointment from the public calendar. I have no problem with personal calendar because i know if its deleted from it then it directly goes to deleted folder where i can read it and delete it from my database too. I have a problem that user want to use public calendar. i can read from their public calendar and import into my database. the problem comes when he delete something from public calendar and it don't goes to deleted folder and i have no way to find out which one he deleted. I used synchronization but from this approach i cant get any deleted itemid too. can anyone help me please.
    Tuesday, December 15, 2009 3:20 AM

Answers

  • Sure there is a Extended Mapi property PR_Deleted_On that tells you when a item is deleted if you create a searchfilter based on this property it should work eg

            Dim iv As ItemView = New ItemView(1000)
            Dim PR_DELETED_ON As New ExtendedPropertyDefinition(26255, MapiPropertyType.SystemTime)
            iv.Traversal = ItemTraversal.SoftDeleted
            Dim sf1 As New SearchFilter.IsGreaterThan(PR_DELETED_ON, DateTime.Now.AddMinutes(-30))
            Dim items As FindItemsResults(Of Item) = tfTargetFolder.FindItems(sf1, iv)
            For Each it As Item In items.items
                Console.WriteLine(it.Subject)
            Next

    Cheers
    Glen

    • Marked as answer by Exchange_buddy Thursday, December 17, 2009 2:40 AM
    Wednesday, December 16, 2009 6:36 AM

All replies

  • When you delete something from a public folder it should go into the dumpster of that public folder so in EWS you should then be able to do a soft delete traversal to access the properties on the item. eg

                String fpfolderPath = "/LogPublic/fivefold/EWSCal"; 
                Folder PfRoot = Folder.Bind(service, WellKnownFolderName.PublicFoldersRoot);
                String[] faFldArray = fpfolderPath.Split('/');
                Folder tfTargetFolder = PfRoot;
                for (int lint = 1; lint < faFldArray.Length; lint++)
                {
                    FolderView fview = new FolderView(1);
                    SearchFilter sf = new SearchFilter.IsEqualTo(FolderSchema.DisplayName, faFldArray[lint]);
                    FindFoldersResults ffResult = service.FindFolders(tfTargetFolder.Id,sf, fview);
                    if (ffResult.TotalCount == 0)
                    {
                        Console.WriteLine("Folder Not Found");
                    }
                    else
                    {
                        tfTargetFolder = ffResult.Folders[0];
                    }
                }
                ItemView iv = new ItemView(1000);
                iv.Traversal = ItemTraversal.SoftDeleted;
                FindItemsResults<Item> items =  tfTargetFolder.FindItems(iv);
                foreach (Item it in items.Items) {
                    Console.WriteLine(it.Subject.ToString());
                }

    Cheers
    Glen

    Tuesday, December 15, 2009 5:21 AM
  • Hi Glen,
             may be you are right but i cannot test your code as i do coding only in Vb and i converted your code in to vb.net but every time i found "folder not found " can you look at my code or i assume there is no such folder in my client exchange server.

         Dim service As ExchangeService = New ExchangeService(ExchangeVersion.Exchange2007_SP1)
            service.Credentials = New NetworkCredential(username, userpass, domain)
            service.Url = New Uri(strurl)

            Dim fpfolderPath As String
            fpfolderPath = "/LogPublic/fivefold/EWSCal"
            Dim PfRoot As Folder
            PfRoot = Folder.Bind(service, WellKnownFolderName.PublicFoldersRoot)
            Dim faFldArray As String() = fpfolderPath.Split("/")
            Dim tfTargetFolder As Folder = PfRoot
            For lint As Integer = 1 To UBound(faFldArray)
                '            Dim fview As FolderView(1)
                Dim sf As SearchFilter
                sf = New SearchFilter.IsEqualTo(FolderSchema.DisplayName, faFldArray(lint))
                Dim ffResult As FindFoldersResults = service.FindFolders(tfTargetFolder.Id, sf, New FolderView(1))
                If (ffResult.TotalCount = 0) Then
                    Console.WriteLine("Folder Not Found")
                Else
                    tfTargetFolder = ffResult.Folders(0)
                End If
            Next
            Dim iv As ItemView = New ItemView(1000)
            iv.Traversal = ItemTraversal.SoftDeleted
            Dim items As FindItemsResults(Of Item) = tfTargetFolder.FindItems(iv)
            For Each it As Item In items.items
                Console.WriteLine(it.Subject)
            Next
      
    Tuesday, December 15, 2009 5:52 PM
  • Your code is okay I only used the code to get the folder to show how to get the FolderID and bind to it if you didn't know it if you have the folderid then you should be able to just bind to it and use the softdelete traversal

            Dim iv As ItemView = New ItemView(1000)
            iv.Traversal = ItemTraversal.SoftDeleted
            Dim items As FindItemsResults(Of Item) = tfTargetFolder.FindItems(iv)
            For Each it As Item In items.items
                Console.WriteLine(it.Subject)
            Next

    Otherwise if you put the path to the folder your working with in

    fpfolderPath = "/LogPublic/fivefold/EWSCal"

    then it will probably work eg this is just the path from root / if the folder is located under a hierarchy of other folders  eg /Firstleve/secondlevel/folder. Or use what other method your confortable with to bind to the Folder

    Cheers
    Glen
    Tuesday, December 15, 2009 11:29 PM
  • Thanx Glen, now its working but i have only one tricky problem basically i want to get those deleted record which been deleted in last 30 min. can i modify my query in this code or i can use order by date n compare each record less than 30 minute. please advice me then my problem would be solve
    Wednesday, December 16, 2009 5:35 AM
  • Sure there is a Extended Mapi property PR_Deleted_On that tells you when a item is deleted if you create a searchfilter based on this property it should work eg

            Dim iv As ItemView = New ItemView(1000)
            Dim PR_DELETED_ON As New ExtendedPropertyDefinition(26255, MapiPropertyType.SystemTime)
            iv.Traversal = ItemTraversal.SoftDeleted
            Dim sf1 As New SearchFilter.IsGreaterThan(PR_DELETED_ON, DateTime.Now.AddMinutes(-30))
            Dim items As FindItemsResults(Of Item) = tfTargetFolder.FindItems(sf1, iv)
            For Each it As Item In items.items
                Console.WriteLine(it.Subject)
            Next

    Cheers
    Glen

    • Marked as answer by Exchange_buddy Thursday, December 17, 2009 2:40 AM
    Wednesday, December 16, 2009 6:36 AM
  • Thanks Glen, it works but it doesn't work when i delete any occurrence of recurring series. suppose i create recurring series for 3 days and i deleted second appointment, then in this case the code not tell me which occurrence has been deleted.
    Wednesday, December 16, 2009 4:31 PM
  • This is because of the complex nature of these type of objects when you delete an occurrence of an appointment no object is deleted rather the master instance in altered and deleted occurrences are generally stored as hidden attachments on the master instance. I would suggest you have a read of http://msdn.microsoft.com/en-us/library/cc500377.aspx which explains in detail the how recurring objects work.

    Cheers
    Glen
    Thursday, December 17, 2009 1:27 AM
  • Thanx Glen, I already have this book and i go through from all chapter. I will find out other way but rest of things is working fine.
    Thursday, December 17, 2009 2:40 AM
  • Glen, I have a question regarding this ticket. basically i understand that when you delete anything from public folder it would not goes to deleted folder and i cant read the appointment. I stored all appointment in my database and i want as it delete from Exchange server it would delete from my database and everything working fine except recurring series. when i delete recurring series i get the unique id of that master recurring series but change key is coming only 5 character length. I want to see full change key so i can match it in my database and delete that record . it work fine for non recurring series.

     can you answer it quickly please...
    Thursday, December 17, 2009 6:23 AM
  • If you have the ItemId why don't you just try to bind to the Item (getItem) and get the latest change key, my suggestion would be that you dont rely on ItemID or Changekey create your own sync property and stored it in a custom property on the items your syncing. Then if the items move or the ItemID's change for whatever reason you will still be able to search and find the item your syncing based on your own custom syncid.

    Cheers
    Glen
    Friday, December 18, 2009 4:47 AM
  • Thanks for your reply Glen, basically i only can read data from my client Exchange server by using EWS API and i am not allow to change anything on their Exchange Server. I am saving all those appointments in my database with itemid and change key. so in case if someone delete any appointment from their calendar i check by its itemid , i used findappointment function which bring all data with respect to date range and it dont bring master recurring record it all bring its occurrences.  The code which you provided me , does'nt work for recurring series because if u delete such recurring series from normal calendar folder then it move to deleted folder and changekey would be same in this way.  once you delete recurring series from public folder then it would disappear and with your code it give me itemid and when i try to bind this itemid with appointment then it give me error that object doest found.

    Dim Appointment As Appointment = Appointment.Bind(service, aitemId, props)

    any suggestion.
    Friday, December 18, 2009 5:01 AM
  • I can see two problems with that approach one pain point with EWS on 2007 is that you can only search for deleted Items but not retrieve an item once its deleted with GetItem. So you can't Get a Deleted Item but you can seach and retrieve properties from the deleted Item with a soft deleted traversal. The other problem is that when you move an item the ItemId and Change key will change so this a poor choice of property to use for Syncronization this is why i would suggest you use your own custom property. Short of that you might want to try the PR_Search_key which should be unique and not change eg

            Dim iv As ItemView = New ItemView(1000)
            Dim PR_DELETED_ON As New ExtendedPropertyDefinition(26255, MapiPropertyType.SystemTime)
            Dim PR_SEARCH_KEY As New ExtendedPropertyDefinition(12299, MapiPropertyType.Binary)
            Dim pset As New PropertySet(BasePropertySet.FirstClassProperties)
            pset.Add(PR_SEARCH_KEY)
            iv.PropertySet = pset
            iv.Traversal = ItemTraversal.SoftDeleted
            Dim sf1 As New SearchFilter.IsGreaterThan(PR_DELETED_ON, DateTime.Now.AddMinutes(-900))
            Dim items As FindItemsResults(Of Item) = tfTargetFolder.FindItems(sf1, iv)
            For Each it As Item In items.items
                Console.WriteLine(it.Subject)
                Console.WriteLine(BitConverter.ToString(it.ExtendedProperties(0).Value))
    
            Next
    Cheers
    Glen
    Friday, December 18, 2009 9:14 AM
  • Thanx Glen, Sorry for late reply and i have a question that this will give me unique id for each appointment and it would not change ever even i change it or delete it.
    Tuesday, December 22, 2009 3:37 AM
  • The Search_key should be okay but maybe for appointments a better option is the cleanglobalobjectID have a read of http://blogs.msdn.com/mstehle/archive/2009/09/02/ews-uid-not-always-the-same-for-orphaned-instances-of-the-same-meeting.aspx

    Cheers
    Glen
    Tuesday, December 22, 2009 6:40 AM