3

I need to save a Guid per every Appointment. I've tried to use PolicyTag and ArchiveTag,but got an ,

"The property PolicyTag is valid only for Exchange Exchange2013 or later versions.",

exception.

Does we have something similar for Exchange 2010? As i understand there is appointment.ID that contains self-generated id. I prefer not to use it. Thank you.

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
mikabuka
  • 237
  • 3
  • 11

1 Answers1

4

A way to deal with this problem is to create an extended property, and put guid for the appointment, and it wont change unless you made a copy from another appointment (after all it is just a property)

private static readonly PropertyDefinitionBase AppointementIdPropertyDefinition = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings, "AppointmentID", MapiPropertyType.String);
public static PropertySet PropertySet = new PropertySet(BasePropertySet.FirstClassProperties, AppointementIdPropertyDefinition);


//Setting the property for the appointment 
 public static void SetGuidForAppointement(Appointment appointment)
{
    try
    {
        appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, Guid.NewGuid().ToString());
        appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
    }
    catch (Exception ex)
    {
        // logging the exception
    }
}

//Getting the property for the appointment
 public static string GetGuidForAppointement(Appointment appointment)
{
    var result = "";
    try
    {
        appointment.Load(PropertySet);
        foreach (var extendedProperty in appointment.ExtendedProperties)
        {
            if (extendedProperty.PropertyDefinition.Name == "AppointmentID")
            {
                result = extendedProperty.Value.ToString();
            }
        }
    }
    catch (Exception ex)
    {
     // logging the exception
    }
    return result;
} 

This solution works very well in case of single appointments and in case of using On Premises Exchange. The problem here is in case of meetings in the Online Web Access (OWA), such as Office 365, the properties of the appointments are copied from the original appointment from the organizer, among these properties are the extended properties which the AppoinmentID is among them. Therefore to avoid this trouble, we make the appointment id of the attendee similar one to the original in the organizer, and just add the email address for the service owner (the service that produced the notification). Without this solution , the appointment in the internal system will have similar AppointmentID to the original booking and it will be considered as one, or you might have different two appointments with the same ID.

 private static void SetGuidForMeetingAppiontment(Appointment appointment)
        {
            var log = "";

            try
            {
                if (!appointment.IsMeeting) return;
                if (appointment.Service.ImpersonatedUserId == null) return;

                /*
                 * The only tricky case is that if the appointment is created at the attendee with no Guid.
                 * In this case the application should look for the original appointment from the organizer's side, and get its guid, to paste it in the new booking 
                 * from the attendee side, and add the attendee emailAddress. 
                 */
                if (GetGuidForMeetingAppointement(appointment).Length <= 36)
                {
                    // If it was an attendee, then look for the original appointment from the organizer's service
                    if (appointment.Service.ImpersonatedUserId.Id != appointment.Organizer.Address)
                    {

                        log += "1/5 Getting the original event of the meeting\n";
                        if (ExchangeLiteService.Services.ContainsKey(appointment.Organizer.Address))
                        {
                            //  FindItemsResults<Appointment> originalAppointments;
                            var originalAppointments = ExchangeLiteService.Services[appointment.Organizer.Address].FindAppointments(WellKnownFolderName.Calendar, new CalendarView(appointment.Start, appointment.End, 1));
                            if (originalAppointments == null) return; //there must be an original appointment. 
                            if (!originalAppointments.Any()) return; //there must be an original appointment. 
                            var originalAppointment = originalAppointments.First(); // there should be only one appointment at a specifict time and date. 

                            log += "2/5 Getting the Guid for the original event of the meeting\n";
                            var originalAppointmentID = GetGuidForMeetingAppointement(originalAppointment);
                            if (string.IsNullOrEmpty(originalAppointmentID)) return; // the original appointment must have a guid already.

                            var orignalAppointmentIDGuid = originalAppointmentID.Substring(0, 36);

                            log += "3/5 Attaching the email address to the guid extracted\n";
                            var newAppointmentID = orignalAppointmentIDGuid + "_" + appointment.Service.ImpersonatedUserId.Id;

                            log += "4/5 Setting the new Guid to the meeting appointment\n";
                            appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, newAppointmentID);

                            log += "5/5 Updateing the meeting appointment\n";
                            appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
                        }
                        else //Then the user is invited from an organizer outside the system.
                        {
                            // Delete if there are anything similar 
                            ExchangeLiteService.OnCallDeleteBookingFromInternal(appointment.Service.ImpersonatedUserId.Id, appointment.Start, appointment.End);

                            //Assign a new 
                            var appointmentIDGuid = Guid.NewGuid().ToString();
                            var newAppointmentID = appointmentIDGuid + "_" + appointment.Service.ImpersonatedUserId.Id;
                            appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, newAppointmentID);
                            appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);

                        }
                        //Get only the guid part of it (without the address of the organizer) 
                    }
                    else // if he was the organizer 
                    {
                        log += "If it was new meeting appointment and the notification from the organizer\n";
                        var appointmentIDGuid = Guid.NewGuid().ToString();
                        var newAppointmentID = appointmentIDGuid + "_" + appointment.Service.ImpersonatedUserId.Id;
                        appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, newAppointmentID);
                        appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
                    }
                }
                else
                {
                    log += "If it was an updated meeting appointment and has Guid already\n";
                    var appointmentID = GetGuidForMeetingAppointement(appointment);
                    var appointmentIDGuid = appointmentID.Substring(0, 36);
                    var newAppointmentID = appointmentIDGuid + "_" + appointment.Service.ImpersonatedUserId.Id;
                    appointment.SetExtendedProperty((ExtendedPropertyDefinition)AppointementIdPropertyDefinition, newAppointmentID);
                    appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
                }
            }
            catch (Exception ex)
            {
                //Logging the exception
            }

        }
Ahmad ElMadi
  • 2,507
  • 21
  • 36
  • Thank for this code. I tried it and it is working very well. Does that mean that I can't use appointment uniqueId to update,cancel appointments because it'll change after a while? Can I use this GUID to bind to a meeting and then update it,cancel it ? – Morano88 Jun 05 '13 at 05:44
  • 1
    regarding the unique id , yes you can use it , but it is quite hard to track it especially if the appointment moved to another folder, for example if it got deleted, although the notification you get is from the same appointment, but the UniqueID will change, therefore if you were mapping your the uniqueID as a primary key, you will have two rows for the same appointment – Ahmad ElMadi Jun 05 '13 at 08:37
  • 2
    In case of meeting, it is different and tricky part. As once the organizer creates a meeting and sends it to the attendees, it actually sends copies of the same appointment object to the attendees. Once the appointment is created, of course the extended property ("AppointmentID") will be Empty, and then will be sent to the attendees. In this case (in case of new created) it is ok, because it the attendees treats the appointments as new ones and put their own GUID. But! once the organizer changes the meeting, it will override all the properties for the rest, including our "AppointmentID" !!! – Ahmad ElMadi Jun 05 '13 at 09:01
  • 2
    This problem happened to me in case of OWA (Online Web Access) for example if you are using office 365. I did not encounter this problem with "On Premises" version of outlook ( I do not know why) Anyway if you want me to help you with that , please once you face the problem make a new post , and I will share the solution I used. So more people can benefit and I get more points ;) – Ahmad ElMadi Jun 05 '13 at 09:04
  • Thanks for your detailed answer but the most important thing for me is the ability to bind to the appointment. So can I bind to the appointment using your generated ID ? and how ? – Morano88 Jun 05 '13 at 11:04
  • 2
    Well here I use the Guid in order to map between appointments of exchange and appointments in internal database , or internal calendar. But you can always on Notifications bind to appointments using the Unique ID . My solution here solves the problem of those who want to sync Exchange appointments with ones in their systems. I have updated my answer above and added the code. Now this is the solution from my side and according to our needs, it might differ than the way you need it or you solve it. but have a look , try to understand the concept I put many comments almost after each line. – Ahmad ElMadi Jun 05 '13 at 11:50