Search code examples
vb.netexchangewebservicesews-managed-api

How to Accept and add Categories to RequiredAttendees Appointments using Exchange Web Services


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

Solution

  • 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