Skip to content

Commit

Permalink
Merge pull request #13072 from nextcloud/bugfix/noid/sync-room-props-…
Browse files Browse the repository at this point in the history
…on-join

fix(federation): Sync room properties on join
  • Loading branch information
nickvergessen authored Aug 22, 2024
2 parents 5e850a1 + 9dda21c commit ee75447
Show file tree
Hide file tree
Showing 19 changed files with 346 additions and 45 deletions.
11 changes: 11 additions & 0 deletions docs/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ See the general [Nextcloud Developers - Events](https://docs.nextcloud.com/serve
* After event: `OCA\Talk\Events\LobbyModifiedEvent`
* Since: 18.0.0

### Federated conversation synced

When multiple properties of a federated conversation are synced, the individual
"Conversation modified" and "Lobby modified" events are still triggered, but a
listener could decide to not follow up individual but only after all properties
where modified.

* Before event: `OCA\Talk\Events\BeforeRoomSyncedEvent`
* After event: `OCA\Talk\Events\RoomSyncedEvent`
* Since: 20.0.0

### Call started

* Before event: `OCA\Talk\Events\BeforeCallStartedEvent`
Expand Down
4 changes: 4 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
use OCA\Talk\Events\BeforeParticipantModifiedEvent;
use OCA\Talk\Events\BeforeRoomDeletedEvent;
use OCA\Talk\Events\BeforeRoomsFetchEvent;
use OCA\Talk\Events\BeforeRoomSyncedEvent;
use OCA\Talk\Events\BeforeSessionLeftRoomEvent;
use OCA\Talk\Events\BeforeUserJoinedRoomEvent;
use OCA\Talk\Events\BotDisabledEvent;
Expand All @@ -61,6 +62,7 @@
use OCA\Talk\Events\RoomCreatedEvent;
use OCA\Talk\Events\RoomDeletedEvent;
use OCA\Talk\Events\RoomModifiedEvent;
use OCA\Talk\Events\RoomSyncedEvent;
use OCA\Talk\Events\SessionLeftRoomEvent;
use OCA\Talk\Events\SystemMessageSentEvent;
use OCA\Talk\Events\SystemMessagesMultipleSentEvent;
Expand Down Expand Up @@ -286,6 +288,8 @@ public function register(IRegistrationContext $context): void {
$context->registerEventListener(CallEndedForEveryoneEvent::class, SignalingListener::class);
$context->registerEventListener(GuestsCleanedUpEvent::class, SignalingListener::class);
$context->registerEventListener(LobbyModifiedEvent::class, SignalingListener::class);
$context->registerEventListener(BeforeRoomSyncedEvent::class, SignalingListener::class);
$context->registerEventListener(RoomSyncedEvent::class, SignalingListener::class);

$context->registerEventListener(ChatMessageSentEvent::class, SignalingListener::class);
$context->registerEventListener(SystemMessageSentEvent::class, SignalingListener::class);
Expand Down
14 changes: 10 additions & 4 deletions lib/Controller/RoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -1660,6 +1660,10 @@ public function joinRoom(string $token, string $password = '', bool $force = tru
return new DataResponse([], Http::STATUS_NOT_FOUND);
}

/** @var TalkRoom $data */
$data = $response->getData();
$this->roomService->syncPropertiesFromHostRoom($room, $data);

$proxyHeaders = $response->getHeaders();
if (isset($proxyHeaders['X-Nextcloud-Talk-Proxy-Hash'])) {
$headers['X-Nextcloud-Talk-Proxy-Hash'] = $proxyHeaders['X-Nextcloud-Talk-Proxy-Hash'];
Expand All @@ -1675,8 +1679,8 @@ public function joinRoom(string $token, string $password = '', bool $force = tru
* The session id can be null only for requests from Talk < 20.
*
* @param string $token Token of the room
* @param string $sessionId Federated session id to join with
* @return DataResponse<Http::STATUS_OK, array<empty>, array{X-Nextcloud-Talk-Hash: string}>|DataResponse<Http::STATUS_NOT_FOUND, null, array{}>
* @param ?string $sessionId Federated session id to join with
* @return DataResponse<Http::STATUS_OK, TalkRoom, array{X-Nextcloud-Talk-Hash: string}>|DataResponse<Http::STATUS_NOT_FOUND, null, array{}>
*
* 200: Federated user joined the room
* 404: Room not found
Expand Down Expand Up @@ -1711,14 +1715,16 @@ public function joinFederatedRoom(string $token, ?string $sessionId): DataRespon
if ($session instanceof Session) {
$this->sessionService->updateLastPing($session, $this->timeFactory->getTime());
}
} else {
$participant = $this->participantService->getParticipantByActor($room, Attendee::ACTOR_FEDERATED_USERS, $this->federationAuthenticator->getCloudId());
}

// Let the clients know if they need to reload capabilities
$capabilities = $this->capabilities->getCapabilities();
return new DataResponse([], Http::STATUS_OK, [
return new DataResponse($this->formatRoom($room, $participant), Http::STATUS_OK, [
'X-Nextcloud-Talk-Hash' => sha1(json_encode($capabilities)),
]);
} catch (RoomNotFoundException|UnauthorizedException) {
} catch (RoomNotFoundException|ParticipantNotFoundException|UnauthorizedException) {
$response = new DataResponse(null, Http::STATUS_NOT_FOUND);
$response->throttle(['token' => $token, 'action' => 'talkFederationAccess']);
return $response;
Expand Down
2 changes: 1 addition & 1 deletion lib/Events/ARoomModifiedEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ abstract class ARoomModifiedEvent extends ARoomEvent {
public const PROPERTY_LISTABLE = 'listable';
public const PROPERTY_LOBBY = 'lobby';
public const PROPERTY_MESSAGE_EXPIRATION = 'messageExpiration';
public const PROPERTY_MENTION_PERMISSIONS = 'mentionPermissions';
public const PROPERTY_NAME = 'name';
public const PROPERTY_PASSWORD = 'password';
public const PROPERTY_READ_ONLY = 'readOnly';
public const PROPERTY_RECORDING_CONSENT = 'recordingConsent';
public const PROPERTY_SIP_ENABLED = 'sipEnabled';
public const PROPERTY_TYPE = 'type';
public const PROPERTY_MENTION_PERMISSIONS = 'mentionPermissions';

/**
* @param self::PROPERTY_* $property
Expand Down
13 changes: 13 additions & 0 deletions lib/Events/ARoomSyncedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Talk\Events;

abstract class ARoomSyncedEvent extends ARoomEvent {
public const PROPERTY_LAST_ACTIVITY = 'lastActivity';
}
12 changes: 12 additions & 0 deletions lib/Events/BeforeRoomSyncedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Talk\Events;

class BeforeRoomSyncedEvent extends ARoomSyncedEvent {
}
30 changes: 30 additions & 0 deletions lib/Events/RoomSyncedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Talk\Events;

use OCA\Talk\Room;

class RoomSyncedEvent extends ARoomSyncedEvent {
/**
* @param array<array-key, ARoomModifiedEvent::PROPERTY_*|ARoomSyncedEvent::PROPERTY_*> $properties
*/
public function __construct(
Room $room,
protected array $properties,
) {
parent::__construct($room);
}

/**
* @return array<array-key, ARoomModifiedEvent::PROPERTY_*|ARoomSyncedEvent::PROPERTY_*>
*/
public function getProperties(): array {
return $this->properties;
}
}
8 changes: 8 additions & 0 deletions lib/Federation/CloudFederationProviderTalk.php
Original file line number Diff line number Diff line change
Expand Up @@ -322,20 +322,28 @@ private function roomModified(int $remoteAttendeeId, array $notification): array
}
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_AVATAR) {
$this->roomService->setAvatar($room, $notification['newValue']);
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_CALL_RECORDING) {
$this->roomService->setCallRecording($room, $notification['newValue']);
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_DESCRIPTION) {
$this->roomService->setDescription($room, $notification['newValue']);
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_IN_CALL) {
$this->roomService->setActiveSince($room, null, $room->getActiveSince(), $notification['newValue'], true);
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_LOBBY) {
$dateTime = !empty($notification['dateTime']) ? \DateTime::createFromFormat('U', $notification['dateTime']) : null;
$this->roomService->setLobby($room, $notification['newValue'], $dateTime, $notification['timerReached'] ?? false);
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_MENTION_PERMISSIONS) {
$this->roomService->setMentionPermissions($room, $notification['newValue']);
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_MESSAGE_EXPIRATION) {
$this->roomService->setMessageExpiration($room, $notification['newValue']);
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_NAME) {
$this->roomService->setName($room, $notification['newValue'], $notification['oldValue']);
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_READ_ONLY) {
$this->roomService->setReadOnly($room, $notification['newValue']);
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_RECORDING_CONSENT) {
/** @psalm-suppress InvalidArgument */
$this->roomService->setRecordingConsent($room, $notification['newValue']);
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_SIP_ENABLED) {
$this->roomService->setSIPEnabled($room, $notification['newValue']);
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_TYPE) {
$this->roomService->setType($room, $notification['newValue']);
} else {
Expand Down
7 changes: 6 additions & 1 deletion lib/Federation/Proxy/TalkV1/Controller/RoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,12 @@ public function joinFederatedRoom(Room $room, Participant $participant): DataRes

$headers = ['X-Nextcloud-Talk-Proxy-Hash' => $this->proxy->overwrittenRemoteTalkHash($proxy->getHeader('X-Nextcloud-Talk-Hash'))];

return new DataResponse([], $statusCode, $headers);
/** @var TalkRoom[] $data */
$data = $this->proxy->getOCSData($proxy);

$data = $this->userConverter->convertAttendee($room, $data, 'actorType', 'actorId', 'displayName');

return new DataResponse($data, $statusCode, $headers);
}

/**
Expand Down
8 changes: 8 additions & 0 deletions lib/Federation/Proxy/TalkV1/Notifier/RoomModifiedListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,25 @@ public function handle(Event $event): void {
if (!in_array($event->getProperty(), [
ARoomModifiedEvent::PROPERTY_ACTIVE_SINCE,
ARoomModifiedEvent::PROPERTY_AVATAR,
ARoomModifiedEvent::PROPERTY_CALL_RECORDING,
ARoomModifiedEvent::PROPERTY_DESCRIPTION,
ARoomModifiedEvent::PROPERTY_IN_CALL,
ARoomModifiedEvent::PROPERTY_LOBBY,
ARoomModifiedEvent::PROPERTY_MENTION_PERMISSIONS,
ARoomModifiedEvent::PROPERTY_MESSAGE_EXPIRATION,
ARoomModifiedEvent::PROPERTY_NAME,
ARoomModifiedEvent::PROPERTY_READ_ONLY,
ARoomModifiedEvent::PROPERTY_RECORDING_CONSENT,
ARoomModifiedEvent::PROPERTY_SIP_ENABLED,
ARoomModifiedEvent::PROPERTY_TYPE,
], true)) {
return;
}

if ($event->getRoom()->isFederatedConversation()) {
return;
}

$participants = $this->participantService->getParticipantsByActorType($event->getRoom(), Attendee::ACTOR_FEDERATED_USERS);
foreach ($participants as $participant) {
$cloudId = $this->cloudIdManager->resolveCloudId($participant->getAttendee()->getActorId());
Expand Down
5 changes: 5 additions & 0 deletions lib/Recording/Listener.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

use OCA\Talk\AppInfo\Application;
use OCA\Talk\Events\ACallEndedEvent;
use OCA\Talk\Events\ARoomEvent;
use OCA\Talk\Events\CallEndedEvent;
use OCA\Talk\Events\CallEndedForEveryoneEvent;
use OCA\Talk\Events\RoomDeletedEvent;
Expand Down Expand Up @@ -42,6 +43,10 @@ public function handle(Event $event): void {
return;
}

if ($event instanceof ARoomEvent && $event->getRoom()->isFederatedConversation()) {
return;
}

match (get_class($event)) {
RoomDeletedEvent::class => $this->roomDeleted($event),
CallEndedEvent::class,
Expand Down
Loading

0 comments on commit ee75447

Please sign in to comment.