diff --git a/spec/controllers/events_spec.cr b/spec/controllers/events_spec.cr index 22750940..8d84aec0 100644 --- a/spec/controllers/events_spec.cr +++ b/spec/controllers/events_spec.cr @@ -1846,10 +1846,11 @@ describe Events, tags: ["event"] do cancelled.size.should eq 1 resent.size.should eq 1 - # the room is invited by the new meeting, and the old host stays involved + # the room is invited by the new meeting, and the previous host is not, + # as the attendees sent no longer list them invited = JSON.parse(resent.first)["attendees"].as_a.map { |attendee| attendee["emailAddress"]["address"].as_s.downcase } invited.should contain "room1@example.com" - invited.should contain organiser + invited.should_not contain organiser # the meeting keeps its identity in PlaceOS: same metadata record, so # visitors, check-in state and extension data survive the move @@ -1865,6 +1866,29 @@ describe Events, tags: ["event"] do payload["previous_host_email"].as_s.should eq organiser end + it "keeps the previous host in the moved meeting when the attendees sent still list them" do + event_id, _, _, _ = create_event.call(true) + new_host = "another-host@example.com" + EventsHelper.stub_calendar_write_access(new_host) + WebMock.stub(:delete, "https://graph.microsoft.com/v1.0/users/dev%40acaprojects.onmicrosoft.com/calendar/events/#{URI.encode_path_segment(event_id)}") + .to_return(status: 204, body: "") + resent = [] of String + WebMock.stub(:post, "https://graph.microsoft.com/v1.0/users/#{URI.encode_path_segment(new_host)}/calendar/events") + .to_return do |request| + resent << (request.body.try(&.gets_to_end) || "") + HTTP::Client::Response.new(201, body: EventsHelper.mock_event_id("evt-moved-with-old-host", "ical-moved-002", recurring: false, organizer: new_host).to_json) + end + + resp = client.patch("#{EVENTS_BASE}/#{event_id}?system_id=#{system_id}", headers: headers, + body: EventsHelper.reassign_host_input(host: new_host, extra_attendee: organiser)) + resp.status_code.should eq(200) + + resent.size.should eq 1 + invited = JSON.parse(resent.first)["attendees"].as_a.map { |attendee| attendee["emailAddress"]["address"].as_s.downcase } + invited.should contain organiser + invited.should contain new_host + end + it "supersedes a reassignment when the meeting moves to the new host" do event_id, changed_bodies, _, _ = create_event.call(true) diff --git a/src/controllers/events.cr b/src/controllers/events.cr index 84fe608c..49364300 100644 --- a/src/controllers/events.cr +++ b/src/controllers/events.cr @@ -635,18 +635,20 @@ class Events < Application update_attendees = !changes.attendees.nil? attendees = changes.attendees.try(&.map { |a| a.email.downcase }) || existing_attendees - # Ensure the host is configured to be attending the meeting and has accepted the meeting - unless host.in?(attendees) + # Handing the meeting to someone else means moving it to their calendar, + # which Office365 only allows by cancelling and re-sending it (PPT-2375). + new_organiser = requested_organiser(changes, organiser: host, requester: user_email) + + # Ensure the host is configured to be attending the meeting and has accepted the meeting. + # A meeting moving to a new host is sent with the attendees as given, so the + # previous host is only invited when the request still lists them. + unless new_organiser || host.in?(attendees) host_attendee = PlaceCalendar::Event::Attendee.new(name: host, email: host, response_status: "accepted") host_attendee.visit_expected = true changes.attendees << host_attendee attendees << host end - # Handing the meeting to someone else means moving it to their calendar, - # which Office365 only allows by cancelling and re-sending it (PPT-2375). - new_organiser = requested_organiser(changes, organiser: host, requester: user_email) - # Naming a host without moving the meeting is recorded against the event # metadata instead, for deployments where the organiser is a room or a # service account rather than a person. A transfer supersedes it: the new