mirror of
https://github.com/nextcloud/spreed.git
synced 2025-12-17 21:12:20 +01:00
feat(pinned): Implement federation support for pinned messages
Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
parent
be7996be4f
commit
3c9828fb34
10 changed files with 167 additions and 8 deletions
|
|
@ -793,8 +793,6 @@ class ChatManager {
|
|||
$comment->setMetaData($metaData);
|
||||
$this->commentsManager->save($comment);
|
||||
|
||||
$this->participantService->resetHiddenPinnedId($chat, (int)$comment->getId());
|
||||
|
||||
$this->roomService->setLastPinnedId($chat, (int)$comment->getId());
|
||||
|
||||
$this->attachmentService->createAttachmentEntryGeneric(
|
||||
|
|
|
|||
|
|
@ -1729,12 +1729,13 @@ class ChatController extends AEnvironmentAwareOCSController {
|
|||
* @psalm-param non-negative-int $messageId
|
||||
* @param int $pinUntil Unix timestamp when to unpin the message
|
||||
* @psalm-param non-negative-int $pinUntil
|
||||
* @return DataResponse<Http::STATUS_OK, ?TalkChatMessageWithParent, array{X-Chat-Last-Common-Read?: numeric-string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND, array{error: 'message'|'until'}, array{}>
|
||||
* @return DataResponse<Http::STATUS_OK, ?TalkChatMessageWithParent, array{X-Chat-Last-Common-Read?: numeric-string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND, array{error: 'message'|'until'|'status'}, array{}>
|
||||
*
|
||||
* 200: Message was pinned successfully
|
||||
* 400: Message could not be pinned
|
||||
* 404: Message was not found
|
||||
*/
|
||||
#[FederationSupported]
|
||||
#[PublicPage]
|
||||
#[RequireModeratorParticipant]
|
||||
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
|
||||
|
|
@ -1744,7 +1745,11 @@ class ChatController extends AEnvironmentAwareOCSController {
|
|||
'messageId' => '[0-9]+',
|
||||
])]
|
||||
public function pinMessage(int $messageId, int $pinUntil = 0): DataResponse {
|
||||
// FIXME add federation
|
||||
if ($this->room->isFederatedConversation()) {
|
||||
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController $proxy */
|
||||
$proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController::class);
|
||||
return $proxy->pinMessage($this->room, $this->participant, $messageId, $pinUntil);
|
||||
}
|
||||
|
||||
try {
|
||||
$comment = $this->chatManager->getComment($this->room, (string)$messageId);
|
||||
|
|
@ -1774,11 +1779,13 @@ class ChatController extends AEnvironmentAwareOCSController {
|
|||
*
|
||||
* @param int $messageId ID of the message
|
||||
* @psalm-param non-negative-int $messageId
|
||||
* @return DataResponse<Http::STATUS_OK, ?TalkChatMessageWithParent, array{X-Chat-Last-Common-Read?: numeric-string}>|DataResponse<Http::STATUS_NOT_FOUND, array{error: 'message'}, array{}>
|
||||
* @return DataResponse<Http::STATUS_OK, ?TalkChatMessageWithParent, array{X-Chat-Last-Common-Read?: numeric-string}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'status'}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{error: 'message'}, array{}>
|
||||
*
|
||||
* 200: Message is not pinned now
|
||||
* 400: Federation request answered with an unknown status code
|
||||
* 404: Message was not found
|
||||
*/
|
||||
#[FederationSupported]
|
||||
#[PublicPage]
|
||||
#[RequireModeratorParticipant]
|
||||
#[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)]
|
||||
|
|
@ -1788,7 +1795,11 @@ class ChatController extends AEnvironmentAwareOCSController {
|
|||
'messageId' => '[0-9]+',
|
||||
])]
|
||||
public function unpinMessage(int $messageId): DataResponse {
|
||||
// FIXME add federation
|
||||
if ($this->room->isFederatedConversation()) {
|
||||
/** @var \OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController $proxy */
|
||||
$proxy = \OCP\Server::get(\OCA\Talk\Federation\Proxy\TalkV1\Controller\ChatController::class);
|
||||
return $proxy->unpinMessage($this->room, $this->participant, $messageId);
|
||||
}
|
||||
|
||||
try {
|
||||
$comment = $this->chatManager->getComment($this->room, (string)$messageId);
|
||||
|
|
@ -1813,6 +1824,7 @@ class ChatController extends AEnvironmentAwareOCSController {
|
|||
* 200: Pinned message is now hidden
|
||||
* 404: Message was not found
|
||||
*/
|
||||
#[FederationSupported]
|
||||
#[PublicPage]
|
||||
#[RequireModeratorOrNoLobby]
|
||||
#[RequireParticipant]
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ abstract class ARoomModifiedEvent extends ARoomEvent {
|
|||
public const PROPERTY_DEFAULT_PERMISSIONS = 'defaultPermissions';
|
||||
public const PROPERTY_DESCRIPTION = 'description';
|
||||
public const PROPERTY_IN_CALL = 'inCall';
|
||||
public const PROPERTY_LAST_PINNED_ID = 'lastPinnedId';
|
||||
public const PROPERTY_LISTABLE = 'listable';
|
||||
public const PROPERTY_LOBBY = 'lobby';
|
||||
public const PROPERTY_LIVE_TRANSCRIPTION_LANGUAGE_ID = 'liveTranscriptionLanguageId';
|
||||
|
|
|
|||
|
|
@ -532,6 +532,7 @@ class BackendNotifier {
|
|||
ARoomModifiedEvent::PROPERTY_DEFAULT_PERMISSIONS => $room->getDefaultPermissions(),
|
||||
ARoomModifiedEvent::PROPERTY_DESCRIPTION => $room->getDescription(),
|
||||
ARoomModifiedEvent::PROPERTY_IN_CALL => $room->getCallFlag(),
|
||||
ARoomModifiedEvent::PROPERTY_LAST_PINNED_ID => $room->getLastPinnedId(),
|
||||
ARoomModifiedEvent::PROPERTY_MENTION_PERMISSIONS => $room->getMentionPermissions(),
|
||||
ARoomModifiedEvent::PROPERTY_MESSAGE_EXPIRATION => $room->getMessageExpiration(),
|
||||
ARoomModifiedEvent::PROPERTY_NAME => $room->getName(),
|
||||
|
|
|
|||
|
|
@ -408,6 +408,8 @@ class CloudFederationProviderTalk implements ICloudFederationProvider, ISignedCl
|
|||
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_MENTION_PERMISSIONS) {
|
||||
/** @psalm-suppress InvalidArgument */
|
||||
$this->roomService->setMentionPermissions($room, $notification['newValue']);
|
||||
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_LAST_PINNED_ID) {
|
||||
$this->roomService->setLastPinnedId($room, $notification['newValue']);
|
||||
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_MESSAGE_EXPIRATION) {
|
||||
$this->roomService->setMessageExpiration($room, $notification['newValue']);
|
||||
} elseif ($notification['changedProperty'] === ARoomModifiedEvent::PROPERTY_NAME) {
|
||||
|
|
|
|||
|
|
@ -313,6 +313,85 @@ class ChatController {
|
|||
return new DataResponse($data, Http::STATUS_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK, ?TalkChatMessageWithParent, array{X-Chat-Last-Common-Read?: numeric-string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND, array{error: 'message'|'until'|'status'}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Message was pinned successfully
|
||||
* 400: Message could not be pinned
|
||||
* 404: Message was not found
|
||||
*
|
||||
* @see \OCA\Talk\Controller\ChatController::pinMessage()
|
||||
*/
|
||||
public function pinMessage(Room $room, Participant $participant, int $messageId, int $pinUntil): DataResponse {
|
||||
$proxy = $this->proxy->post(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/' . $messageId . '/pin',
|
||||
['pinUntil' => $pinUntil]
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if ($statusCode !== Http::STATUS_OK) {
|
||||
if ($statusCode !== Http::STATUS_NOT_FOUND) {
|
||||
$statusCode = $this->proxy->logUnexpectedStatusCode(__METHOD__, $statusCode);
|
||||
$data = ['error' => 'status'];
|
||||
return new DataResponse($data, $statusCode);
|
||||
}
|
||||
/** @var array{error: 'message'|'until'|'status'} $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_NOT_FOUND, Http::STATUS_BAD_REQUEST]);
|
||||
return new DataResponse($data, $statusCode);
|
||||
}
|
||||
|
||||
/** @var ?TalkChatMessageWithParent $data */
|
||||
$data = $this->proxy->getOCSData($proxy, default: null);
|
||||
if (!empty($data)) {
|
||||
/** @var TalkChatMessageWithParent $data */
|
||||
$data = $this->userConverter->convertMessage($room, $data);
|
||||
}
|
||||
|
||||
return new DataResponse($data, Http::STATUS_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK, ?TalkChatMessageWithParent, array{X-Chat-Last-Common-Read?: numeric-string}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'status'}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{error: 'message'}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Message is not pinned now
|
||||
* 400: Federation request answered with an unknown status code
|
||||
* 404: Message was not found
|
||||
*
|
||||
* @see \OCA\Talk\Controller\ChatController::unpinMessage()
|
||||
*/
|
||||
public function unpinMessage(Room $room, Participant $participant, int $messageId): DataResponse {
|
||||
$proxy = $this->proxy->delete(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/' . $messageId . '/pin',
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if ($statusCode !== Http::STATUS_OK) {
|
||||
if ($statusCode !== Http::STATUS_NOT_FOUND) {
|
||||
$statusCode = $this->proxy->logUnexpectedStatusCode(__METHOD__, $statusCode);
|
||||
$data = ['error' => 'status'];
|
||||
return new DataResponse($data, $statusCode);
|
||||
}
|
||||
/** @var array{error: 'message'} $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_NOT_FOUND]);
|
||||
return new DataResponse($data, Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
/** @var ?TalkChatMessageWithParent $data */
|
||||
$data = $this->proxy->getOCSData($proxy, default: null);
|
||||
if (!empty($data)) {
|
||||
/** @var TalkChatMessageWithParent $data */
|
||||
$data = $this->userConverter->convertMessage($room, $data);
|
||||
}
|
||||
|
||||
return new DataResponse($data, Http::STATUS_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK|Http::STATUS_ACCEPTED, TalkChatMessageWithParent, array{X-Chat-Last-Common-Read?: numeric-string}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: string}, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND|Http::STATUS_METHOD_NOT_ALLOWED|Http::STATUS_REQUEST_ENTITY_TOO_LARGE, array{error: string}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ class RoomModifiedListener implements IEventListener {
|
|||
ARoomModifiedEvent::PROPERTY_DEFAULT_PERMISSIONS,
|
||||
ARoomModifiedEvent::PROPERTY_DESCRIPTION,
|
||||
ARoomModifiedEvent::PROPERTY_IN_CALL,
|
||||
ARoomModifiedEvent::PROPERTY_LAST_PINNED_ID,
|
||||
ARoomModifiedEvent::PROPERTY_LOBBY,
|
||||
ARoomModifiedEvent::PROPERTY_MENTION_PERMISSIONS,
|
||||
ARoomModifiedEvent::PROPERTY_MESSAGE_EXPIRATION,
|
||||
|
|
|
|||
|
|
@ -230,10 +230,13 @@ class ProxyRequest {
|
|||
}
|
||||
|
||||
/**
|
||||
* @template T of array<empty>|null
|
||||
* @param list<int> $allowedStatusCodes
|
||||
* @param T $default
|
||||
* @return array|T
|
||||
* @throws CannotReachRemoteException
|
||||
*/
|
||||
public function getOCSData(IResponse $response, array $allowedStatusCodes = [Http::STATUS_OK]): array {
|
||||
public function getOCSData(IResponse $response, array $allowedStatusCodes = [Http::STATUS_OK], ?array $default = []): ?array {
|
||||
if (!in_array($response->getStatusCode(), $allowedStatusCodes, true)) {
|
||||
$this->logUnexpectedStatusCode(__METHOD__, $response->getStatusCode());
|
||||
}
|
||||
|
|
@ -249,6 +252,6 @@ class ProxyRequest {
|
|||
throw new CannotReachRemoteException('Error parsing JSON response', $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return $responseData['ocs']['data'] ?? [];
|
||||
return $responseData['ocs']['data'] ?? $default;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -883,6 +883,9 @@ class RoomService {
|
|||
}
|
||||
|
||||
public function setLastPinnedId(Room $room, int $lastPinnedId): void {
|
||||
$event = new BeforeRoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_LAST_PINNED_ID, $lastPinnedId);
|
||||
$this->dispatcher->dispatchTyped($event);
|
||||
|
||||
$update = $this->db->getQueryBuilder();
|
||||
$update->update('talk_rooms')
|
||||
->set('last_pinned_id', $update->createNamedParameter($lastPinnedId))
|
||||
|
|
@ -890,6 +893,10 @@ class RoomService {
|
|||
$update->executeStatement();
|
||||
|
||||
$room->setLastPinnedId($lastPinnedId);
|
||||
$this->participantService->resetHiddenPinnedId($room, $lastPinnedId);
|
||||
|
||||
$event = new RoomModifiedEvent($room, ARoomModifiedEvent::PROPERTY_LAST_PINNED_ID, $lastPinnedId);
|
||||
$this->dispatcher->dispatchTyped($event);
|
||||
}
|
||||
|
||||
public function setAssignedSignalingServer(Room $room, ?int $signalingServer): bool {
|
||||
|
|
@ -1362,6 +1369,10 @@ class RoomService {
|
|||
$this->logger->error('An error (' . $e->getReason() . ') occurred while trying to sync mentionPermissions of ' . $local->getId() . ' to ' . $host['mentionPermissions'], ['exception' => $e]);
|
||||
}
|
||||
}
|
||||
if (isset($host['lastPinnedId']) && $host['lastPinnedId'] !== $local->getLastPinnedId()) {
|
||||
$this->setLastPinnedId($local, $host['lastPinnedId']);
|
||||
$changed[] = ARoomModifiedEvent::PROPERTY_LAST_PINNED_ID;
|
||||
}
|
||||
if (isset($host['messageExpiration']) && $host['messageExpiration'] !== $local->getMessageExpiration()) {
|
||||
try {
|
||||
$this->setMessageExpiration($local, $host['messageExpiration']);
|
||||
|
|
|
|||
|
|
@ -556,3 +556,54 @@ Feature: federation/chat
|
|||
Then user "participant2" sees the following shared location in room "LOCAL::room" with 200
|
||||
| room | actorType | actorId | actorDisplayName | message | messageParameters |
|
||||
| LOCAL::room | federated_users | participant1@{$LOCAL_URL} | participant1-displayname | {object} | "IGNORE" |
|
||||
|
||||
Scenario: Pin handling as a federated user
|
||||
Given user "participant1" creates room "room" (v4)
|
||||
| roomType | 2 |
|
||||
| roomName | room |
|
||||
And user "participant1" adds federated_user "participant2" to room "room" with 200 (v4)
|
||||
And using server "REMOTE"
|
||||
And user "participant2" has the following invitations (v1)
|
||||
| remoteServerUrl | remoteToken | state | inviterCloudId | inviterDisplayName |
|
||||
| LOCAL | room | 0 | participant1@LOCAL | participant1-displayname |
|
||||
And user "participant2" accepts invite to room "room" of server "LOCAL" with 200 (v1)
|
||||
| id | name | type | remoteServer | remoteToken |
|
||||
| LOCAL::room | room | 2 | LOCAL | room |
|
||||
Then user "participant2" is participant of the following rooms (v4)
|
||||
| id | type |
|
||||
| LOCAL::room | 2 |
|
||||
|
||||
And using server "LOCAL"
|
||||
When user "participant1" sends message "Message 1" to room "room" with 201
|
||||
When user "participant1" pins message "Message 1" in room "room" with 200
|
||||
And using server "REMOTE"
|
||||
Then user "participant2" is participant of the following rooms (v4)
|
||||
| id | type | lastPinnedId | hiddenPinnedId |
|
||||
| LOCAL::room | 2 | Message 1 | EMPTY |
|
||||
When user "participant2" hides pinned message "Message 1" in room "LOCAL::room" with 200
|
||||
Then user "participant2" is participant of the following rooms (v4)
|
||||
| id | type | lastPinnedId | hiddenPinnedId |
|
||||
| LOCAL::room | 2 | Message 1 | Message 1 |
|
||||
|
||||
# Unpinning resets lastPinnedId
|
||||
And using server "LOCAL"
|
||||
When user "participant1" unpins message "Message 1" in room "room" with 200
|
||||
And using server "REMOTE"
|
||||
Then user "participant2" is participant of the following rooms (v4)
|
||||
| id | type | lastPinnedId | hiddenPinnedId |
|
||||
| LOCAL::room | 2 | EMPTY | Message 1 |
|
||||
|
||||
# Pin temporarily
|
||||
And using server "LOCAL"
|
||||
When user "participant1" pins message "Message 1" for 3 seconds in room "room" with 200
|
||||
And using server "REMOTE"
|
||||
Then user "participant2" is participant of the following rooms (v4)
|
||||
| id | type | lastPinnedId | hiddenPinnedId |
|
||||
| LOCAL::room | 2 | Message 1 | EMPTY |
|
||||
When wait for 4 seconds
|
||||
And using server "LOCAL"
|
||||
And run "OCA\Talk\BackgroundJob\UnpinMessage" background jobs
|
||||
And using server "REMOTE"
|
||||
Then user "participant2" is participant of the following rooms (v4)
|
||||
| id | type | lastPinnedId | hiddenPinnedId |
|
||||
| LOCAL::room | 2 | EMPTY | EMPTY |
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue