4

I’m using ExchangeService(ExchangeVersion.Exchange2010_SP1)

I want to Accept and add Categories to RequiredAttendees Appointments. To do this i need to find these Appointments.

My understanding in EWS is that on Save of an Appointment that has RequiredAttendees a new Meeting Request is created for each of the ‘required attendees’.

How can I access the Appointments that were created automatically for the ‘required attendees’? These are shown in the required attendees Calendars as Appointments, along with a Meeting Request.

I have managed to do a crude find on the Subject (steps below)

  1. Connect to server as Organiser
  2. Create Appointment
  3. Set Subject
  4. Add Required Attendee
  5. Save Appointment

  6. Connect to server as Required Attendee from step 4

  7. Find Appointment that has Subject at step 3
  8. Add Categories to Appointment at step 7
  9. Update Appointment at step 7
  10. Accept Appointment at step 7

And this does work, but concerned users will change the Subject.

I have tried adding an Extended Property and value to the Appointment created by the Organiser and then FindItems for the Extended Property value in the Appointments connected as the Required Attendee. This does not work.

Is there a preferred method for what I’m trying to accomplish?

Thank you

Private Shared ReadOnly m_organiserEmailAddress As String = "Organiser@test.com"
Private Shared ReadOnly m_eventIdExtendedPropertyDefinition As New ExtendedPropertyDefinition(DefaultExtendedPropertySet.Meeting, "EventId", MapiPropertyType.Long)

'--> start here
Public Shared Function SaveToOutlookCalendar(eventCalendarItem As EventCalendarItem) As String

    If eventCalendarItem.Id Is Nothing Then
        'new
        Dim newAppointment = EventCalendarItemMapper.SaveNewAppointment(eventCalendarItem)
        'set the Id
        eventCalendarItem.Id = newAppointment.Id.UniqueId.ToString()
        'accept the calendar item on behalf of the Attendee
        EventCalendarItemMapper.AcceptAppointmentAsAttendees(newAppointment)
        Return eventCalendarItem.Id
    Else
        'update existing appointment
        Return EventCalendarItemMapper.UpdateAppointment(eventCalendarItem)
    End If


End Function

Private Shared Sub ConnectToServer(Optional autoUser As String = "")

    If autoUser = "" Then
        _service.Url = New Uri(ExchangeWebServicesUrl)
    Else
        _service.AutodiscoverUrl(autoUser)
    End If

End Sub

Private Shared Sub ImpersonateUser(userEmail As String)

    _service.Credentials = New NetworkCredential(ImpersonatorUsername, ImpersonatorPassword, Domain)
    _service.ImpersonatedUserId = New ImpersonatedUserId(ConnectingIdType.SmtpAddress, userEmail)

End Sub

Private Shared Function SaveNewAppointment(eventCalendarItem As EventCalendarItem) As Appointment

    Try
        ConnectToServer(m_organiserEmailAddress)
        ImpersonateUser(m_organiserEmailAddress)

        Dim appointment As New Appointment(_service) With {
                   .Subject = eventCalendarItem.Subject}

        'add attendees
        For Each attendee In eventCalendarItem.Attendees
            appointment.RequiredAttendees.Add(attendee.Email)
        Next

        'add categories
        For Each category In eventCalendarItem.Categories
            appointment.Categories.Add(Globals.GetEnumDescription(category))
        Next

        'add EventId = 5059 as an extended property of the appointment
        appointment.SetExtendedProperty(m_eventIdExtendedPropertyDefinition, 5059)

        appointment.Save(SendInvitationsMode.SendOnlyToAll)

        Return appointment
    Catch
        Throw New Exception("Can't save appointment")
    End Try


End Function

Private Shared Sub AcceptAppointmentAsAttendees(appointment As Appointment)

    For Each attendee In appointment.RequiredAttendees
        Try
            ConnectToServer(attendee.Address.ToString())
            ImpersonateUser(attendee.Address.ToString())

            For Each a In FindRelatedAppiontments(appointment)
                a.Categories.Add(Globals.GetEnumDescription(CalendarItemCategory.Workshop))
                a.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone)
                a.Accept(True)
            Next

        Catch
            Throw
        End Try
    Next
End Sub

Private Shared Function FindRelatedAppiontments(appointment As Appointment) As List(Of Appointment)

    Dim view As New ItemView(1000)
    Dim foundAppointments As New List(Of Appointment)

    view.PropertySet =
        New PropertySet(New PropertyDefinitionBase() {m_eventIdExtendedPropertyDefinition})

    'Extended Property value = 5059
    Dim searchFilter = New SearchFilter.IsEqualTo(m_eventIdExtendedPropertyDefinition, 5059)

    For Each a In _service.FindItems(WellKnownFolderName.Calendar, searchFilter, view)
        If a.ExtendedProperties.Count > 0 Then
            foundAppointments.Add(appointment.Bind(_service, CType(a.Id, ItemId)))
        End If
    Next

    Return foundAppointments

End Function
  • Why the extended property did not work ? did they get copied from the organizer to the attendees ? – Ahmad ElMadi Feb 14 '13 at 10:12
  • @BraveHeart Unfortunately no, so, if at Step 6 I connect to the server as the Organiser, set up a SearchFilter for FindItems for my Extended Property value the Appointment is returned. Using the same code but connecting to the server as a Required Attendee no Appointments are returned. I have tried in FindItems WellKnownFolderName.Inbox for the Meeting Request and the WellKnownFolderName.Calendar for the Appointment. – Edward Trenchard Feb 14 '13 at 11:32
  • To be honest I do not think I really go what you mean 100%, or why you want that . Maybe if you put some of your code to explain how you connect to a server as an Organizer or an attendee. Do you have two applications for that ? I might suggest you to use the [Streaming Notifications](http://msdn.microsoft.com/en-us/library/exchange/hh312849(v=exchg.140).aspx) so you can detect appointments coming to the attendee's calendar. – Ahmad ElMadi Feb 14 '13 at 12:26
  • @BraveHeart i have included some code. If i ConnectToServer with Organiser email address in AcceptAppointmentAsAttendees then the Appointment with the Extended Property value = 5059 is retured, but if i connect as in the example as the Required Attendee email address no appointment is returned - thus why i think the Extended Property is not copied to the Required Attendee Appointment or Meeting Request. I was hoping that what i'm doing, getting the Appointments and/or Meeting Requests created for RequiredAttendees from an Appointment would be straightforward... – Edward Trenchard Feb 14 '13 at 14:18

1 Answers1

2

Well one thing is for sure that there is nothing straight forward in the EWS. I will be honest with that, that till the moment, I did not work with integrating from the interior calendar to the Exchange calendar, my experience is the opposite , which is get what in the exchange to the internal one.

Anyway after reading your code, I think you are almost there. However I suggest that you catch the appointments that reach to the attendee by using the Streaming Notifications it is not that hard! So I would say the steps should be like this

  1. Create the appointment
  2. Apply The extended property thing ( I suggest to use the GUID instead of hard coded number) as follows

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;
} 
  1. Catch the appointment with the StreamingNotificationEvent. In my opinion a good way to do that is to run both Organizer and Attendee in the same time and catch the appointments between them. To see an example, I have posted an answer to a previous question Multiple impersonation-threads in Exchange Web Service (EWS) . Please vote for the answers of both posts (here and there) if you found my answers are useful.

I do not want to make you scared, but once you solve your current problem; if you want to continue with it will get more complicated. I could write down how I solved the meetings problem, but I do not see it straight forward at all so it might be better if you write your own.

Cheers

Community
  • 1
  • 1
Ahmad ElMadi
  • 2,507
  • 21
  • 36
  • thank you, i'm going to take a look at your suggestion this weekend, i'll let you know how i get on. i'm not too scared yet ;-) – Edward Trenchard Feb 15 '13 at 16:15
  • sorry it's been a while. I got back to this project and setting up a Streaming Notification with an Event Handler on the creation of the Calendar item for the Required Attendee has done the trick. Thank you. I need to tidy the code up, once done will post solution and vote you up. – Edward Trenchard Apr 03 '13 at 09:54