UPSTREAM BASELINE: nextcloud/spreed v22.0.12 (без изменений)
Type checking / changes (push) Has been cancelled
Type checking / test (push) Has been cancelled
Type checking / typescript-summary (push) Has been cancelled
Node tests / changes (push) Has been cancelled
Node tests / test (push) Has been cancelled
Node tests / test-summary (push) Has been cancelled
Type checking / changes (push) Has been cancelled
Type checking / test (push) Has been cancelled
Type checking / typescript-summary (push) Has been cancelled
Node tests / changes (push) Has been cancelled
Node tests / test (push) Has been cancelled
Node tests / test-summary (push) Has been cancelled
Источник: https://github.com/nextcloud/spreed/archive/refs/tags/v22.0.12.tar.gz С этого коммита ветка официального Nextcloud Talk отрезана (решение владельца 2026-07-06). Все дальнейшие изменения — только наши; версии релизов: 22.0.12-f7.N.
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Federation\Proxy\TalkV1\Controller;
|
||||
|
||||
use OCA\Talk\Exceptions\CannotReachRemoteException;
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\ProxyRequest;
|
||||
use OCA\Talk\Model\Invitation;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCA\Talk\Room;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\FileDisplayResponse;
|
||||
use OCP\Files\SimpleFS\InMemoryFile;
|
||||
|
||||
/**
|
||||
* @psalm-import-type TalkChatMentionSuggestion from ResponseDefinitions
|
||||
* @psalm-import-type TalkChatMessageWithParent from ResponseDefinitions
|
||||
*/
|
||||
class AvatarController {
|
||||
public function __construct(
|
||||
protected ProxyRequest $proxy,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\AvatarController::getAvatar()
|
||||
*
|
||||
* @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Room avatar returned
|
||||
*/
|
||||
public function getAvatar(Room $room, ?Participant $participant, ?Invitation $invitation, bool $darkTheme): FileDisplayResponse {
|
||||
if ($participant === null && $invitation === null) {
|
||||
throw new CannotReachRemoteException('Must receive either participant or invitation');
|
||||
}
|
||||
|
||||
$proxy = $this->proxy->get(
|
||||
$participant ? $participant->getAttendee()->getInvitedCloudId() : $invitation->getLocalCloudId(),
|
||||
$participant ? $participant->getAttendee()->getAccessToken() : $invitation->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/room/' . $room->getRemoteToken() . '/avatar' . ($darkTheme ? '/dark' : ''),
|
||||
);
|
||||
|
||||
if ($proxy->getStatusCode() !== Http::STATUS_OK) {
|
||||
$this->proxy->logUnexpectedStatusCode(__METHOD__, $proxy->getStatusCode(), (string)$proxy->getBody());
|
||||
throw new CannotReachRemoteException('Avatar request had unexpected status code');
|
||||
}
|
||||
|
||||
$content = $proxy->getBody();
|
||||
if ($content === '') {
|
||||
throw new CannotReachRemoteException('No avatar content received');
|
||||
}
|
||||
|
||||
$file = new InMemoryFile($room->getToken(), $content);
|
||||
|
||||
$response = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $file->getMimeType()]);
|
||||
// Cache for 1 day
|
||||
$response->cacheFor(60 * 60 * 24, false, true);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\AvatarController::getUserProxyAvatar()
|
||||
*
|
||||
* @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: User avatar returned
|
||||
*/
|
||||
public function getUserProxyAvatar(string $remoteServer, string $user, int $size, bool $darkTheme): FileDisplayResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
null,
|
||||
null,
|
||||
$remoteServer . '/index.php/avatar/' . $user . '/' . $size . ($darkTheme ? '/dark' : ''),
|
||||
);
|
||||
|
||||
if ($proxy->getStatusCode() !== Http::STATUS_OK) {
|
||||
if ($proxy->getStatusCode() !== Http::STATUS_NOT_FOUND) {
|
||||
$this->proxy->logUnexpectedStatusCode(__METHOD__, $proxy->getStatusCode(), (string)$proxy->getBody());
|
||||
}
|
||||
throw new CannotReachRemoteException('Avatar request had unexpected status code');
|
||||
}
|
||||
|
||||
$content = $proxy->getBody();
|
||||
if ($content === '') {
|
||||
throw new CannotReachRemoteException('No avatar content received');
|
||||
}
|
||||
|
||||
$file = new InMemoryFile($user, $content);
|
||||
|
||||
$response = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $file->getMimeType()]);
|
||||
// Cache for 1 day
|
||||
$response->cacheFor(60 * 60 * 24, false, true);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Federation\Proxy\TalkV1\Controller;
|
||||
|
||||
use OCA\Talk\Exceptions\CannotReachRemoteException;
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\ProxyRequest;
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\UserConverter;
|
||||
use OCA\Talk\Model\Session;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCA\Talk\Room;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
|
||||
/**
|
||||
* @psalm-import-type TalkCapabilities from ResponseDefinitions
|
||||
* @psalm-import-type TalkCallPeer from ResponseDefinitions
|
||||
* @psalm-import-type TalkParticipant from ResponseDefinitions
|
||||
* @psalm-import-type TalkRoom from ResponseDefinitions
|
||||
*/
|
||||
class CallController {
|
||||
public function __construct(
|
||||
protected ProxyRequest $proxy,
|
||||
protected UserConverter $userConverter,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\CallController::getPeersForCall()
|
||||
*
|
||||
* @param Room $room the federated room to get the call peers
|
||||
* @param Participant $participant the federated user to get the call peers
|
||||
* @return DataResponse<Http::STATUS_OK, list<TalkCallPeer>, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: List of peers in the call returned
|
||||
*/
|
||||
public function getPeersForCall(Room $room, Participant $participant): DataResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v4/call/' . $room->getRemoteToken(),
|
||||
);
|
||||
|
||||
/** @var list<TalkCallPeer> $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
|
||||
/** @var list<TalkCallPeer> $data */
|
||||
$data = $this->userConverter->convertAttendees($room, $data, 'actorType', 'actorId', 'displayName');
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if (!in_array($statusCode, [Http::STATUS_OK], true)) {
|
||||
$this->proxy->logUnexpectedStatusCode(__METHOD__, $proxy->getStatusCode());
|
||||
throw new CannotReachRemoteException();
|
||||
}
|
||||
|
||||
return new DataResponse($data, $statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\CallController::joinFederatedCall()
|
||||
*
|
||||
* @param Room $room the federated room to join the call in
|
||||
* @param Participant $participant the federated user that will join the
|
||||
* call; the participant must have a session
|
||||
* @param int<0, 15> $flags In-Call flags
|
||||
* @psalm-param int-mask-of<Participant::FLAG_*> $flags
|
||||
* @param bool $silent Join the call silently
|
||||
* @param bool $recordingConsent Agreement to be recorded
|
||||
* @return DataResponse<Http::STATUS_OK|Http::STATUS_NOT_FOUND, null, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: string}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Federated user is now in the call
|
||||
* 400: Conditions to join not met
|
||||
* 404: Room not found
|
||||
*/
|
||||
public function joinFederatedCall(Room $room, Participant $participant, int $flags, bool $silent, bool $recordingConsent): DataResponse {
|
||||
$options = [
|
||||
'sessionId' => $participant->getSession()->getSessionId(),
|
||||
'flags' => $flags,
|
||||
'silent' => $silent,
|
||||
'recordingConsent' => $recordingConsent,
|
||||
];
|
||||
|
||||
$proxy = $this->proxy->post(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v4/call/' . $room->getRemoteToken() . '/federation',
|
||||
$options,
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if (!in_array($statusCode, [Http::STATUS_OK, Http::STATUS_BAD_REQUEST, Http::STATUS_NOT_FOUND], true)) {
|
||||
$this->proxy->logUnexpectedStatusCode(__METHOD__, $proxy->getStatusCode());
|
||||
throw new CannotReachRemoteException();
|
||||
}
|
||||
|
||||
if ($statusCode === Http::STATUS_BAD_REQUEST) {
|
||||
/** @var array{error: string} $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_BAD_REQUEST]);
|
||||
return new DataResponse($data, $statusCode);
|
||||
}
|
||||
|
||||
return new DataResponse(null, $statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\CallController::ringAttendee()
|
||||
*
|
||||
* @param int $attendeeId ID of the attendee to ring
|
||||
* @return DataResponse<Http::STATUS_OK|Http::STATUS_NOT_FOUND, null, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: string}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Attendee rang successfully
|
||||
* 400: Ringing attendee is not possible
|
||||
* 404: Attendee could not be found
|
||||
*/
|
||||
public function ringAttendee(Room $room, Participant $participant, int $attendeeId): DataResponse {
|
||||
$proxy = $this->proxy->post(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v4/call/' . $room->getRemoteToken() . '/ring/' . $attendeeId,
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if (!in_array($statusCode, [Http::STATUS_OK, Http::STATUS_BAD_REQUEST, Http::STATUS_NOT_FOUND], true)) {
|
||||
$this->proxy->logUnexpectedStatusCode(__METHOD__, $proxy->getStatusCode());
|
||||
throw new CannotReachRemoteException();
|
||||
}
|
||||
|
||||
if ($statusCode === Http::STATUS_BAD_REQUEST) {
|
||||
/** @var array{error: string} $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_BAD_REQUEST]);
|
||||
return new DataResponse($data, $statusCode);
|
||||
}
|
||||
|
||||
return new DataResponse(null, $statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\CallController::updateFederatedCallFlags()
|
||||
*
|
||||
* @param Room $room the federated room to update the call flags in
|
||||
* @param Participant $participant the federated user to update the call
|
||||
* flags; the participant must have a session
|
||||
* @param int<0, 15> $flags New flags
|
||||
* @psalm-param int-mask-of<Participant::FLAG_*> $flags New flags
|
||||
* @return DataResponse<Http::STATUS_OK|Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND, null, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: In-call flags updated successfully for federated user
|
||||
* 400: Updating in-call flags is not possible
|
||||
* 404: Room not found
|
||||
*/
|
||||
public function updateFederatedCallFlags(Room $room, Participant $participant, int $flags): DataResponse {
|
||||
$options = [
|
||||
'sessionId' => $participant->getSession()->getSessionId(),
|
||||
'flags' => $flags,
|
||||
];
|
||||
|
||||
$proxy = $this->proxy->put(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v4/call/' . $room->getRemoteToken() . '/federation',
|
||||
$options,
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if (!in_array($statusCode, [Http::STATUS_OK, Http::STATUS_BAD_REQUEST, Http::STATUS_NOT_FOUND], true)) {
|
||||
$this->proxy->logUnexpectedStatusCode(__METHOD__, $proxy->getStatusCode());
|
||||
throw new CannotReachRemoteException();
|
||||
}
|
||||
|
||||
return new DataResponse(null, $statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\CallController::leaveFederatedCall()
|
||||
*
|
||||
* @param Room $room the federated room to leave the call in
|
||||
* @param Participant $participant the federated user that will leave the
|
||||
* call; the participant must have a session
|
||||
* @return DataResponse<Http::STATUS_OK|Http::STATUS_NOT_FOUND, null, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Federated user left the call
|
||||
* 404: Room not found
|
||||
*/
|
||||
public function leaveFederatedCall(Room $room, Participant $participant): DataResponse {
|
||||
$options = [
|
||||
'sessionId' => $participant->getSession()->getSessionId(),
|
||||
];
|
||||
|
||||
$proxy = $this->proxy->delete(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v4/call/' . $room->getRemoteToken() . '/federation',
|
||||
$options,
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if (!in_array($statusCode, [Http::STATUS_OK, Http::STATUS_NOT_FOUND], true)) {
|
||||
$this->proxy->logUnexpectedStatusCode(__METHOD__, $proxy->getStatusCode());
|
||||
throw new CannotReachRemoteException();
|
||||
}
|
||||
|
||||
return new DataResponse(null, $statusCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,539 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Federation\Proxy\TalkV1\Controller;
|
||||
|
||||
use OCA\Talk\CachePrefix;
|
||||
use OCA\Talk\Chat\Notifier;
|
||||
use OCA\Talk\Exceptions\CannotReachRemoteException;
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\ProxyRequest;
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\UserConverter;
|
||||
use OCA\Talk\Model\Attendee;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\RoomFormatter;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\ICache;
|
||||
use OCP\ICacheFactory;
|
||||
|
||||
/**
|
||||
* @psalm-import-type TalkChatMentionSuggestion from ResponseDefinitions
|
||||
* @psalm-import-type TalkChatMessage from ResponseDefinitions
|
||||
* @psalm-import-type TalkChatMessageWithParent from ResponseDefinitions
|
||||
* @psalm-import-type TalkRoom from ResponseDefinitions
|
||||
*/
|
||||
class ChatController {
|
||||
protected ?ICache $proxyCacheMessages;
|
||||
|
||||
public function __construct(
|
||||
protected ProxyRequest $proxy,
|
||||
protected UserConverter $userConverter,
|
||||
protected ParticipantService $participantService,
|
||||
protected RoomFormatter $roomFormatter,
|
||||
protected Notifier $notifier,
|
||||
ICacheFactory $cacheFactory,
|
||||
) {
|
||||
$this->proxyCacheMessages = $cacheFactory->isAvailable() ? $cacheFactory->createDistributed(CachePrefix::FEDERATED_PCM) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\ChatController::sendMessage()
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_CREATED, ?TalkChatMessageWithParent, array{X-Chat-Last-Common-Read?: numeric-string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND|Http::STATUS_REQUEST_ENTITY_TOO_LARGE|Http::STATUS_TOO_MANY_REQUESTS, array{error: string}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 201: Message sent successfully
|
||||
* 400: Sending message is not possible
|
||||
* 404: Actor not found
|
||||
* 413: Message too long
|
||||
* 429: Mention rate limit exceeded (guests only)
|
||||
*/
|
||||
public function sendMessage(Room $room, Participant $participant, string $message, string $referenceId, int $replyTo, bool $silent, string $threadTitle, int $threadId): DataResponse {
|
||||
$proxy = $this->proxy->post(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken(),
|
||||
[
|
||||
'message' => $message,
|
||||
'actorDisplayName' => $participant->getAttendee()->getDisplayName(),
|
||||
'referenceId' => $referenceId,
|
||||
'replyTo' => $replyTo,
|
||||
'silent' => $silent,
|
||||
'threadTitle' => $threadTitle,
|
||||
'threadId' => $threadId,
|
||||
],
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if ($statusCode !== Http::STATUS_CREATED) {
|
||||
if (!in_array($statusCode, [
|
||||
Http::STATUS_BAD_REQUEST,
|
||||
Http::STATUS_NOT_FOUND,
|
||||
Http::STATUS_REQUEST_ENTITY_TOO_LARGE,
|
||||
Http::STATUS_TOO_MANY_REQUESTS,
|
||||
], true)) {
|
||||
$statusCode = $this->proxy->logUnexpectedStatusCode(__METHOD__, $statusCode);
|
||||
}
|
||||
/** @var array{error: string} $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_CREATED]);
|
||||
return new DataResponse($data, $statusCode);
|
||||
}
|
||||
|
||||
/** @var ?TalkChatMessageWithParent $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_CREATED]);
|
||||
if (!empty($data)) {
|
||||
$data = $this->userConverter->convertMessage($room, $data);
|
||||
} else {
|
||||
$data = null;
|
||||
}
|
||||
|
||||
$headers = [];
|
||||
if ($proxy->getHeader('X-Chat-Last-Common-Read')) {
|
||||
$headers['X-Chat-Last-Common-Read'] = (string)(int)$proxy->getHeader('X-Chat-Last-Common-Read');
|
||||
}
|
||||
|
||||
return new DataResponse(
|
||||
$data,
|
||||
Http::STATUS_CREATED,
|
||||
$headers,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK, list<TalkChatMessageWithParent>, array{'X-Chat-Last-Common-Read'?: numeric-string, X-Chat-Last-Given?: numeric-string}>|DataResponse<Http::STATUS_NOT_MODIFIED, null, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Messages returned
|
||||
* 304: No messages
|
||||
*
|
||||
* @see \OCA\Talk\Controller\ChatController::getMessageContext()
|
||||
*/
|
||||
public function receiveMessages(
|
||||
Room $room,
|
||||
Participant $participant,
|
||||
int $lookIntoFuture,
|
||||
int $limit,
|
||||
int $lastKnownMessageId,
|
||||
int $lastCommonReadId,
|
||||
int $timeout,
|
||||
int $setReadMarker,
|
||||
int $includeLastKnown,
|
||||
int $noStatusUpdate,
|
||||
int $markNotificationsAsRead): DataResponse {
|
||||
$cacheKey = sha1(json_encode([$room->getRemoteServer(), $room->getRemoteToken()]));
|
||||
|
||||
|
||||
if ($lookIntoFuture && $markNotificationsAsRead && $participant->getAttendee()->getActorType() === Attendee::ACTOR_USERS) {
|
||||
$this->notifier->markMentionNotificationsRead($room, $participant->getAttendee()->getActorId());
|
||||
}
|
||||
|
||||
if ($lookIntoFuture) {
|
||||
if ($this->proxyCacheMessages instanceof ICache) {
|
||||
for ($i = 0; $i <= $timeout; $i++) {
|
||||
$cacheData = (int)$this->proxyCacheMessages->get($cacheKey);
|
||||
if ($lastKnownMessageId !== $cacheData) {
|
||||
break;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
} else {
|
||||
// Poor-mans timeout, should later on cancel/trigger earlier,
|
||||
// by checking the PCM database table
|
||||
sleep(max(0, $timeout - 5));
|
||||
}
|
||||
}
|
||||
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken(),
|
||||
[
|
||||
'lookIntoFuture' => $lookIntoFuture,
|
||||
'limit' => $limit,
|
||||
'lastKnownMessageId' => $lastKnownMessageId,
|
||||
'lastCommonReadId' => $lastCommonReadId,
|
||||
'timeout' => 0,
|
||||
'setReadMarker' => $setReadMarker,
|
||||
'includeLastKnown' => $includeLastKnown,
|
||||
'noStatusUpdate' => $noStatusUpdate,
|
||||
'markNotificationsAsRead' => $markNotificationsAsRead,
|
||||
],
|
||||
);
|
||||
|
||||
if ($lookIntoFuture && $setReadMarker) {
|
||||
$this->participantService->updateUnreadInfoForProxyParticipant($participant,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
(int)($proxy->getHeader('X-Chat-Last-Given') ?: $lastKnownMessageId),
|
||||
);
|
||||
}
|
||||
|
||||
if ($proxy->getStatusCode() === Http::STATUS_NOT_MODIFIED) {
|
||||
if ($lookIntoFuture && $this->proxyCacheMessages instanceof ICache) {
|
||||
$cacheData = $this->proxyCacheMessages->get($cacheKey);
|
||||
if ($cacheData === null || $cacheData < $lastKnownMessageId) {
|
||||
$this->proxyCacheMessages->set($cacheKey, $lastKnownMessageId, 300);
|
||||
}
|
||||
}
|
||||
return new DataResponse(null, Http::STATUS_NOT_MODIFIED);
|
||||
}
|
||||
|
||||
$headers = [];
|
||||
if ($proxy->getHeader('X-Chat-Last-Common-Read')) {
|
||||
$headers['X-Chat-Last-Common-Read'] = (string)(int)$proxy->getHeader('X-Chat-Last-Common-Read');
|
||||
}
|
||||
if ($proxy->getHeader('X-Chat-Last-Given')) {
|
||||
$headers['X-Chat-Last-Given'] = (string)(int)$proxy->getHeader('X-Chat-Last-Given');
|
||||
if ($lookIntoFuture && $this->proxyCacheMessages instanceof ICache) {
|
||||
$cacheData = $this->proxyCacheMessages->get($cacheKey);
|
||||
if ($cacheData === null || $cacheData < $headers['X-Chat-Last-Given']) {
|
||||
$this->proxyCacheMessages->set($cacheKey, (int)$headers['X-Chat-Last-Given'], 300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @var list<TalkChatMessageWithParent> $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
/** @var list<TalkChatMessageWithParent> $data */
|
||||
$data = $this->userConverter->convertMessages($room, $data);
|
||||
|
||||
return new DataResponse($data, Http::STATUS_OK, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK, list<TalkChatMessageWithParent>, array{'X-Chat-Last-Common-Read'?: numeric-string, X-Chat-Last-Given?: numeric-string}>|DataResponse<Http::STATUS_NOT_MODIFIED, null, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Message context returned
|
||||
* 304: No messages
|
||||
*
|
||||
* @see \OCA\Talk\Controller\ChatController::getMessageContext()
|
||||
*/
|
||||
public function getMessageContext(Room $room, Participant $participant, int $messageId, int $limit): DataResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/' . $messageId . '/context',
|
||||
[
|
||||
'limit' => $limit,
|
||||
],
|
||||
);
|
||||
|
||||
if ($participant->getAttendee()->getActorType() === Attendee::ACTOR_USERS) {
|
||||
$this->notifier->markMentionNotificationsRead($room, $participant->getAttendee()->getActorId());
|
||||
}
|
||||
|
||||
if ($proxy->getStatusCode() === Http::STATUS_NOT_MODIFIED) {
|
||||
return new DataResponse(null, Http::STATUS_NOT_MODIFIED);
|
||||
}
|
||||
|
||||
$headers = [];
|
||||
if ($proxy->getHeader('X-Chat-Last-Common-Read')) {
|
||||
$headers['X-Chat-Last-Common-Read'] = (string)(int)$proxy->getHeader('X-Chat-Last-Common-Read');
|
||||
}
|
||||
if ($proxy->getHeader('X-Chat-Last-Given')) {
|
||||
$headers['X-Chat-Last-Given'] = (string)(int)$proxy->getHeader('X-Chat-Last-Given');
|
||||
}
|
||||
|
||||
/** @var list<TalkChatMessageWithParent> $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
/** @var list<TalkChatMessageWithParent> $data */
|
||||
$data = $this->userConverter->convertMessages($room, $data);
|
||||
|
||||
return new DataResponse($data, Http::STATUS_OK, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK, array<string, list<TalkChatMessage>>, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: List of shared objects messages of each type returned
|
||||
*
|
||||
* @see \OCA\Talk\Controller\ChatController::getObjectsSharedInRoomOverview()
|
||||
*/
|
||||
public function getObjectsSharedInRoomOverview(Room $room, Participant $participant, int $limit): DataResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/share/overview',
|
||||
[
|
||||
'limit' => $limit,
|
||||
],
|
||||
);
|
||||
|
||||
// We allow "200 OK" and "406 Not Acceptable" here, so that 33 against 32 and before is working well
|
||||
/** @var array<string, list<TalkChatMessage>> $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_OK, Http::STATUS_NOT_ACCEPTABLE]);
|
||||
|
||||
$result = [];
|
||||
foreach ($data as $type => $items) {
|
||||
$result[$type] = array_values($this->userConverter->convertMessages($room, $items));
|
||||
}
|
||||
|
||||
/** @var array<string, list<TalkChatMessage>> $result */
|
||||
return new DataResponse($result, Http::STATUS_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK, array<string, TalkChatMessage>, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: List of shared objects messages returned
|
||||
*
|
||||
* @see \OCA\Talk\Controller\ChatController::getObjectsSharedInRoom()
|
||||
*/
|
||||
public function getObjectsSharedInRoom(Room $room, Participant $participant, string $objectType, int $lastKnownMessageId, int $limit): DataResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/share',
|
||||
[
|
||||
'objectType' => $objectType,
|
||||
'lastKnownMessageId' => $lastKnownMessageId,
|
||||
'limit' => $limit,
|
||||
],
|
||||
);
|
||||
|
||||
// We allow "200 OK" and "406 Not Acceptable" here, so that 33 against 32 and before is working well
|
||||
/** @var array<string, TalkChatMessage> $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_OK, Http::STATUS_NOT_ACCEPTABLE]);
|
||||
/** @var array<string, TalkChatMessage> $data */
|
||||
$data = $this->userConverter->convertMessages($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
|
||||
*
|
||||
* 200: Message edited successfully
|
||||
* 202: Message edited successfully, but a bot or Matterbridge is configured, so the information can be replicated to other services
|
||||
* 400: Editing message is not possible, e.g. when the new message is empty or the message is too old
|
||||
* 403: Missing permissions to edit message
|
||||
* 404: Message not found
|
||||
* 405: Editing this message type is not allowed
|
||||
*
|
||||
* @see \OCA\Talk\Controller\ChatController::editMessage()
|
||||
*/
|
||||
public function editMessage(Room $room, Participant $participant, int $messageId, string $message): DataResponse {
|
||||
$proxy = $this->proxy->put(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/' . $messageId,
|
||||
[
|
||||
'message' => $message,
|
||||
],
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if ($statusCode !== Http::STATUS_OK && $statusCode !== Http::STATUS_ACCEPTED) {
|
||||
if (!in_array($statusCode, [
|
||||
Http::STATUS_BAD_REQUEST,
|
||||
Http::STATUS_FORBIDDEN,
|
||||
Http::STATUS_NOT_FOUND,
|
||||
Http::STATUS_METHOD_NOT_ALLOWED,
|
||||
Http::STATUS_REQUEST_ENTITY_TOO_LARGE,
|
||||
], true)) {
|
||||
$statusCode = $this->proxy->logUnexpectedStatusCode(__METHOD__, $statusCode);
|
||||
$data = ['error' => 'status'];
|
||||
} elseif ($statusCode === Http::STATUS_BAD_REQUEST) {
|
||||
/** @var array{error: string} $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_BAD_REQUEST]);
|
||||
} else {
|
||||
$data = [];
|
||||
}
|
||||
return new DataResponse($data, $statusCode);
|
||||
}
|
||||
|
||||
/** @var TalkChatMessageWithParent $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_OK, Http::STATUS_ACCEPTED]);
|
||||
$data = $this->userConverter->convertMessage($room, $data);
|
||||
|
||||
$headers = [];
|
||||
if ($proxy->getHeader('X-Chat-Last-Common-Read')) {
|
||||
$headers['X-Chat-Last-Common-Read'] = (string)(int)$proxy->getHeader('X-Chat-Last-Common-Read');
|
||||
}
|
||||
|
||||
return new DataResponse(
|
||||
$data,
|
||||
$statusCode,
|
||||
$headers,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK|Http::STATUS_ACCEPTED, TalkChatMessageWithParent, array{X-Chat-Last-Common-Read?: numeric-string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND|Http::STATUS_METHOD_NOT_ALLOWED, array{error: string}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Message deleted successfully
|
||||
* 202: Message deleted successfully, but a bot or Matterbridge is configured, so the information can be replicated elsewhere
|
||||
* 400: Deleting message is not possible
|
||||
* 403: Missing permissions to delete message
|
||||
* 404: Message not found
|
||||
* 405: Deleting this message type is not allowed
|
||||
*
|
||||
* @see \OCA\Talk\Controller\ChatController::deleteMessage()
|
||||
*/
|
||||
public function deleteMessage(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,
|
||||
);
|
||||
|
||||
/** @var Http::STATUS_OK|Http::STATUS_ACCEPTED|Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND|Http::STATUS_REQUEST_ENTITY_TOO_LARGE $statusCode */
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
|
||||
if ($statusCode !== Http::STATUS_OK && $statusCode !== Http::STATUS_ACCEPTED) {
|
||||
if (in_array($statusCode, [
|
||||
Http::STATUS_BAD_REQUEST,
|
||||
Http::STATUS_FORBIDDEN,
|
||||
Http::STATUS_NOT_FOUND,
|
||||
Http::STATUS_REQUEST_ENTITY_TOO_LARGE,
|
||||
], true)) {
|
||||
$statusCode = $this->proxy->logUnexpectedStatusCode(__METHOD__, $statusCode);
|
||||
}
|
||||
return new DataResponse([], $statusCode);
|
||||
}
|
||||
|
||||
/** @var TalkChatMessageWithParent $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_OK, Http::STATUS_ACCEPTED]);
|
||||
$data = $this->userConverter->convertMessage($room, $data);
|
||||
|
||||
$headers = [];
|
||||
if ($proxy->getHeader('X-Chat-Last-Common-Read')) {
|
||||
$headers['X-Chat-Last-Common-Read'] = (string)(int)$proxy->getHeader('X-Chat-Last-Common-Read');
|
||||
}
|
||||
|
||||
return new DataResponse(
|
||||
$data,
|
||||
$statusCode,
|
||||
$headers,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\ChatController::setReadMarker()
|
||||
*
|
||||
* @param 'json'|'xml' $responseFormat
|
||||
* @return DataResponse<Http::STATUS_OK, TalkRoom, array{X-Chat-Last-Common-Read?: numeric-string}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: List of mention suggestions returned
|
||||
*/
|
||||
public function setReadMarker(Room $room, Participant $participant, string $responseFormat, ?int $lastReadMessage): DataResponse {
|
||||
$proxy = $this->proxy->post(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/read',
|
||||
$lastReadMessage !== null ? [
|
||||
'lastReadMessage' => $lastReadMessage,
|
||||
] : [],
|
||||
);
|
||||
|
||||
/** @var TalkRoom $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
|
||||
$this->participantService->updateUnreadInfoForProxyParticipant(
|
||||
$participant,
|
||||
$data['unreadMessages'],
|
||||
$data['unreadMention'],
|
||||
$data['unreadMentionDirect'],
|
||||
$data['lastReadMessage'],
|
||||
);
|
||||
|
||||
$headers = $lastCommonRead = [];
|
||||
if ($proxy->getHeader('X-Chat-Last-Common-Read')) {
|
||||
$lastCommonRead[$room->getId()] = (int)$proxy->getHeader('X-Chat-Last-Common-Read');
|
||||
$headers['X-Chat-Last-Common-Read'] = (string)$lastCommonRead[$room->getId()];
|
||||
}
|
||||
|
||||
return new DataResponse($this->roomFormatter->formatRoom(
|
||||
$responseFormat,
|
||||
$lastCommonRead,
|
||||
$room,
|
||||
$participant,
|
||||
), Http::STATUS_OK, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\ChatController::markUnread()
|
||||
*
|
||||
* @param 'json'|'xml' $responseFormat
|
||||
* @return DataResponse<Http::STATUS_OK, TalkRoom, array{X-Chat-Last-Common-Read?: numeric-string}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: List of mention suggestions returned
|
||||
*/
|
||||
public function markUnread(Room $room, Participant $participant, string $responseFormat): DataResponse {
|
||||
$proxy = $this->proxy->delete(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/read',
|
||||
);
|
||||
|
||||
/** @var TalkRoom $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
|
||||
$this->participantService->updateUnreadInfoForProxyParticipant(
|
||||
$participant,
|
||||
$data['unreadMessages'],
|
||||
$data['unreadMention'],
|
||||
$data['unreadMentionDirect'],
|
||||
$data['lastReadMessage'],
|
||||
);
|
||||
|
||||
$headers = $lastCommonRead = [];
|
||||
if ($proxy->getHeader('X-Chat-Last-Common-Read')) {
|
||||
$lastCommonRead[$room->getId()] = (int)$proxy->getHeader('X-Chat-Last-Common-Read');
|
||||
$headers['X-Chat-Last-Common-Read'] = (string)$lastCommonRead[$room->getId()];
|
||||
}
|
||||
|
||||
return new DataResponse($this->roomFormatter->formatRoom(
|
||||
$responseFormat,
|
||||
$lastCommonRead,
|
||||
$room,
|
||||
$participant,
|
||||
), Http::STATUS_OK, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\ChatController::mentions()
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, list<TalkChatMentionSuggestion>, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: List of mention suggestions returned
|
||||
*/
|
||||
public function mentions(Room $room, Participant $participant, string $search, int $limit, bool $includeStatus): DataResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/mentions',
|
||||
[
|
||||
'search' => $search,
|
||||
'limit' => $limit,
|
||||
'includeStatus' => $includeStatus,
|
||||
],
|
||||
);
|
||||
|
||||
/** @var list<TalkChatMentionSuggestion> $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
/** @var list<TalkChatMentionSuggestion> $data */
|
||||
$data = $this->userConverter->convertAttendees($room, $data, 'source', 'id', 'label');
|
||||
|
||||
// FIXME post-load status information
|
||||
return new DataResponse($data, Http::STATUS_OK);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Federation\Proxy\TalkV1\Controller;
|
||||
|
||||
use OCA\Talk\Exceptions\CannotReachRemoteException;
|
||||
use OCA\Talk\Exceptions\PollPropertyException;
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\ProxyRequest;
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\UserConverter;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCA\Talk\Room;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @psalm-import-type TalkPoll from ResponseDefinitions
|
||||
* @psalm-import-type TalkPollDraft from ResponseDefinitions
|
||||
*/
|
||||
class PollController {
|
||||
public function __construct(
|
||||
protected ProxyRequest $proxy,
|
||||
protected UserConverter $userConverter,
|
||||
protected LoggerInterface $logger,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK, list<TalkPollDraft>, array{}>|DataResponse<Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, list<empty>, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Polls returned
|
||||
* 404: Polls not found
|
||||
*
|
||||
* @see \OCA\Talk\Controller\PollController::showPoll()
|
||||
*/
|
||||
public function getDraftsForRoom(Room $room, Participant $participant): DataResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/poll/' . $room->getRemoteToken() . '/drafts',
|
||||
);
|
||||
|
||||
$status = $proxy->getStatusCode();
|
||||
if ($status === Http::STATUS_NOT_FOUND || $status === Http::STATUS_FORBIDDEN) {
|
||||
return new DataResponse([], $status);
|
||||
}
|
||||
|
||||
/** @var list<TalkPollDraft> $list */
|
||||
$list = $this->proxy->getOCSData($proxy);
|
||||
|
||||
$data = [];
|
||||
foreach ($list as $poll) {
|
||||
$data[] = $this->userConverter->convertPoll($room, $poll);
|
||||
}
|
||||
|
||||
return new DataResponse($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK, TalkPoll, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{error: string}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Poll returned
|
||||
* 404: Poll not found
|
||||
*
|
||||
* @see \OCA\Talk\Controller\PollController::showPoll()
|
||||
*/
|
||||
public function showPoll(Room $room, Participant $participant, int $pollId): DataResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/poll/' . $room->getRemoteToken() . '/' . $pollId,
|
||||
);
|
||||
|
||||
if ($proxy->getStatusCode() === Http::STATUS_NOT_FOUND) {
|
||||
/** @var array{error?: string} $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
return new DataResponse(['error' => $data['error'] ?? 'poll'], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
/** @var TalkPoll $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
$data = $this->userConverter->convertPoll($room, $data);
|
||||
|
||||
return new DataResponse($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<int> $optionIds
|
||||
* @return DataResponse<Http::STATUS_OK, TalkPoll, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND, array{error: string}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Voted successfully
|
||||
* 400: Voting is not possible
|
||||
* 404: Poll not found
|
||||
*
|
||||
* @see \OCA\Talk\Controller\PollController::votePoll()
|
||||
*/
|
||||
public function votePoll(Room $room, Participant $participant, int $pollId, array $optionIds): DataResponse {
|
||||
$proxy = $this->proxy->post(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/poll/' . $room->getRemoteToken() . '/' . $pollId,
|
||||
['optionIds' => $optionIds],
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if ($statusCode !== Http::STATUS_OK) {
|
||||
if (!in_array($statusCode, [
|
||||
Http::STATUS_BAD_REQUEST,
|
||||
Http::STATUS_NOT_FOUND,
|
||||
], true)) {
|
||||
$statusCode = $this->proxy->logUnexpectedStatusCode(__METHOD__, $statusCode);
|
||||
}
|
||||
/** @var array{error?: string} $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
return new DataResponse(['error' => $data['error'] ?? 'poll'], $statusCode);
|
||||
}
|
||||
|
||||
/** @var TalkPoll $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
$data = $this->userConverter->convertPoll($room, $data);
|
||||
|
||||
return new DataResponse($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK, TalkPollDraft, array{}>|DataResponse<Http::STATUS_CREATED, TalkPoll, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'draft'|'options'|'poll'|'question'|'room'}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Draft created successfully
|
||||
* 201: Poll created successfully
|
||||
* 400: Creating poll is not possible
|
||||
*
|
||||
* @see \OCA\Talk\Controller\PollController::createPoll()
|
||||
*/
|
||||
public function createPoll(Room $room, Participant $participant, string $question, array $options, int $resultMode, int $maxVotes, bool $draft): DataResponse {
|
||||
$proxy = $this->proxy->post(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/poll/' . $room->getRemoteToken(),
|
||||
[
|
||||
'question' => $question,
|
||||
'options' => $options,
|
||||
'resultMode' => $resultMode,
|
||||
'maxVotes' => $maxVotes,
|
||||
'draft' => $draft,
|
||||
],
|
||||
);
|
||||
|
||||
$status = $proxy->getStatusCode();
|
||||
if ($status === Http::STATUS_BAD_REQUEST) {
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_BAD_REQUEST]);
|
||||
return new DataResponse($data, Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
/** @var TalkPoll $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_OK, Http::STATUS_CREATED]);
|
||||
$data = $this->userConverter->convertPoll($room, $data);
|
||||
|
||||
if ($status === Http::STATUS_OK) {
|
||||
return new DataResponse($data);
|
||||
}
|
||||
return new DataResponse($data, Http::STATUS_CREATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK, TalkPollDraft, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, array{error: 'draft'|'options'|'poll'|'question'|'room'}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Draft created successfully
|
||||
* 201: Poll created successfully
|
||||
* 400: Creating poll is not possible
|
||||
*
|
||||
* @see \OCA\Talk\Controller\PollController::createPoll()
|
||||
*/
|
||||
public function updateDraftPoll(int $pollId, Room $room, Participant $participant, string $question, array $options, int $resultMode, int $maxVotes): DataResponse {
|
||||
$proxy = $this->proxy->post(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/poll/' . $room->getRemoteToken() . '/draft/' . $pollId,
|
||||
[
|
||||
'question' => $question,
|
||||
'options' => $options,
|
||||
'resultMode' => $resultMode,
|
||||
'maxVotes' => $maxVotes
|
||||
],
|
||||
);
|
||||
|
||||
$status = $proxy->getStatusCode();
|
||||
if ($status === Http::STATUS_BAD_REQUEST) {
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_BAD_REQUEST]);
|
||||
return new DataResponse($data, Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
/** @var TalkPollDraft $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_OK, Http::STATUS_CREATED]);
|
||||
$data = $this->userConverter->convertPoll($room, $data);
|
||||
|
||||
if ($status === Http::STATUS_OK) {
|
||||
return new DataResponse($data);
|
||||
}
|
||||
return new DataResponse($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DataResponse<Http::STATUS_OK, TalkPoll, array{}>|DataResponse<Http::STATUS_ACCEPTED, null, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, array{error: 'poll'}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Poll closed successfully
|
||||
* 400: Poll already closed
|
||||
* 403: Missing permissions to close poll
|
||||
* 404: Poll not found
|
||||
*
|
||||
* @see \OCA\Talk\Controller\PollController::closePoll()
|
||||
*/
|
||||
public function closePoll(Room $room, Participant $participant, int $pollId): DataResponse {
|
||||
$proxy = $this->proxy->delete(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/poll/' . $room->getRemoteToken() . '/' . $pollId,
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if ($statusCode !== Http::STATUS_OK) {
|
||||
if (!in_array($statusCode, [
|
||||
Http::STATUS_BAD_REQUEST,
|
||||
Http::STATUS_FORBIDDEN,
|
||||
Http::STATUS_NOT_FOUND,
|
||||
], true)) {
|
||||
$statusCode = $this->proxy->logUnexpectedStatusCode(__METHOD__, $statusCode);
|
||||
}
|
||||
/** @var array{error?: string} $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
|
||||
if ($data['error'] !== PollPropertyException::REASON_POLL) {
|
||||
$this->logger->error('Unhandled error in ' . __METHOD__ . ': ' . $data['error']);
|
||||
}
|
||||
|
||||
return new DataResponse(['error' => PollPropertyException::REASON_POLL], $statusCode);
|
||||
}
|
||||
|
||||
/** @var TalkPoll $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
$data = $this->userConverter->convertPoll($room, $data);
|
||||
|
||||
return new DataResponse($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Federation\Proxy\TalkV1\Controller;
|
||||
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\ProxyRequest;
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\UserConverter;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCA\Talk\Room;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
|
||||
/**
|
||||
* @psalm-import-type TalkReaction from ResponseDefinitions
|
||||
*/
|
||||
class ReactionController {
|
||||
public function __construct(
|
||||
protected ProxyRequest $proxy,
|
||||
protected UserConverter $userConverter,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a reaction to a message
|
||||
*
|
||||
* @param int $messageId ID of the message
|
||||
* @psalm-param non-negative-int $messageId
|
||||
* @param string $reaction Emoji to add
|
||||
* @return DataResponse<Http::STATUS_OK|Http::STATUS_CREATED, array<string, list<TalkReaction>>|\stdClass, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND, null, array{}>
|
||||
*
|
||||
* 200: Reaction already existed
|
||||
* 201: Reaction added successfully
|
||||
* 400: Adding reaction is not possible
|
||||
* 404: Message not found
|
||||
*
|
||||
* @see \OCA\Talk\Controller\ReactionController::react()
|
||||
*/
|
||||
public function react(Room $room, Participant $participant, int $messageId, string $reaction, string $format): DataResponse {
|
||||
$proxy = $this->proxy->post(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/reaction/' . $room->getRemoteToken() . '/' . $messageId,
|
||||
[
|
||||
'reaction' => $reaction,
|
||||
],
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if ($statusCode !== Http::STATUS_OK && $statusCode !== Http::STATUS_CREATED) {
|
||||
if (!in_array($statusCode, [
|
||||
Http::STATUS_BAD_REQUEST,
|
||||
Http::STATUS_NOT_FOUND,
|
||||
], true)) {
|
||||
$statusCode = $this->proxy->logUnexpectedStatusCode(__METHOD__, $statusCode);
|
||||
}
|
||||
return new DataResponse(null, $statusCode);
|
||||
}
|
||||
|
||||
/** @var array<string, list<TalkReaction>> $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_CREATED, Http::STATUS_OK]);
|
||||
$data = $this->userConverter->convertReactionsList($room, $data);
|
||||
|
||||
return new DataResponse($this->formatReactions($format, $data), $statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a reaction from a message
|
||||
*
|
||||
* @param int $messageId ID of the message
|
||||
* @psalm-param non-negative-int $messageId
|
||||
* @param string $reaction Emoji to remove
|
||||
* @return DataResponse<Http::STATUS_OK, array<string, list<TalkReaction>>|\stdClass, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND, null, array{}>
|
||||
*
|
||||
* 200: Reaction deleted successfully
|
||||
* 400: Deleting reaction is not possible
|
||||
* 404: Message not found
|
||||
*
|
||||
* @see \OCA\Talk\Controller\ReactionController::delete()
|
||||
*/
|
||||
public function delete(Room $room, Participant $participant, int $messageId, string $reaction, string $format): DataResponse {
|
||||
$proxy = $this->proxy->delete(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/reaction/' . $room->getRemoteToken() . '/' . $messageId,
|
||||
[
|
||||
'reaction' => $reaction,
|
||||
],
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if ($statusCode !== Http::STATUS_OK) {
|
||||
if (!in_array($statusCode, [
|
||||
Http::STATUS_BAD_REQUEST,
|
||||
Http::STATUS_NOT_FOUND,
|
||||
], true)) {
|
||||
$statusCode = $this->proxy->logUnexpectedStatusCode(__METHOD__, $statusCode);
|
||||
}
|
||||
return new DataResponse(null, $statusCode);
|
||||
}
|
||||
|
||||
/** @var array<string, list<TalkReaction>> $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
$data = $this->userConverter->convertReactionsList($room, $data);
|
||||
|
||||
return new DataResponse($this->formatReactions($format, $data), $statusCode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a list of reactions for a message
|
||||
*
|
||||
* @param int $messageId ID of the message
|
||||
* @psalm-param non-negative-int $messageId
|
||||
* @param string|null $reaction Emoji to filter
|
||||
* @return DataResponse<Http::STATUS_OK, array<string, list<TalkReaction>>|\stdClass, array{}>|DataResponse<Http::STATUS_NOT_FOUND, null, array{}>
|
||||
*
|
||||
* 200: Reactions returned
|
||||
* 404: Message or reaction not found
|
||||
*
|
||||
* @see \OCA\Talk\Controller\ReactionController::getReactions()
|
||||
*/
|
||||
public function getReactions(Room $room, Participant $participant, int $messageId, ?string $reaction, string $format): DataResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/reaction/' . $room->getRemoteToken() . '/' . $messageId,
|
||||
$reaction === null ? [] : [
|
||||
'reaction' => $reaction,
|
||||
],
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if ($statusCode !== Http::STATUS_OK) {
|
||||
if ($statusCode !== Http::STATUS_NOT_FOUND) {
|
||||
$this->proxy->logUnexpectedStatusCode(__METHOD__, $statusCode);
|
||||
}
|
||||
return new DataResponse(null, Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
/** @var array<string, list<TalkReaction>> $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
$data = $this->userConverter->convertReactionsList($room, $data);
|
||||
|
||||
return new DataResponse($this->formatReactions($format, $data), $statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, list<TalkReaction>> $reactions
|
||||
* @return array<string, list<TalkReaction>>|\stdClass
|
||||
*/
|
||||
protected function formatReactions(string $format, array $reactions): array|\stdClass {
|
||||
if ($format === 'json' && empty($reactions)) {
|
||||
// Cheating here to make sure the reactions array is always a
|
||||
// JSON object on the API, even when there is no reaction at all.
|
||||
return new \stdClass();
|
||||
}
|
||||
|
||||
return $reactions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Federation\Proxy\TalkV1\Controller;
|
||||
|
||||
use OCA\Talk\Exceptions\CannotReachRemoteException;
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\ProxyRequest;
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\UserConverter;
|
||||
use OCA\Talk\Model\Session;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCA\Talk\Room;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
|
||||
/**
|
||||
* @psalm-import-type TalkCapabilities from ResponseDefinitions
|
||||
* @psalm-import-type TalkParticipant from ResponseDefinitions
|
||||
* @psalm-import-type TalkRoom from ResponseDefinitions
|
||||
*/
|
||||
class RoomController {
|
||||
public function __construct(
|
||||
protected ProxyRequest $proxy,
|
||||
protected UserConverter $userConverter,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\RoomController::getParticipants()
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, list<TalkParticipant>, array{X-Nextcloud-Has-User-Statuses?: bool}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Participants returned
|
||||
* 403: Missing permissions for getting participants
|
||||
*/
|
||||
public function getParticipants(Room $room, Participant $participant): DataResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v4/room/' . $room->getRemoteToken() . '/participants',
|
||||
);
|
||||
|
||||
/** @var list<TalkParticipant> $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
|
||||
/** @var list<TalkParticipant> $data */
|
||||
$data = $this->userConverter->convertAttendees($room, $data, 'actorType', 'actorId', 'displayName');
|
||||
$headers = [];
|
||||
if ($proxy->getHeader('X-Nextcloud-Has-User-Statuses')) {
|
||||
$headers['X-Nextcloud-Has-User-Statuses'] = (bool)$proxy->getHeader('X-Nextcloud-Has-User-Statuses');
|
||||
}
|
||||
|
||||
return new DataResponse($data, Http::STATUS_OK, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\RoomController::joinFederatedRoom()
|
||||
*
|
||||
* @param Room $room the federated room to join
|
||||
* @param Participant $participant the federated user to will join the room;
|
||||
* the participant must have a session
|
||||
* @return DataResponse<Http::STATUS_OK|Http::STATUS_NOT_FOUND, array<empty>, array{X-Nextcloud-Talk-Proxy-Hash: string}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Federated user joined the room
|
||||
* 404: Room not found
|
||||
*/
|
||||
public function joinFederatedRoom(Room $room, Participant $participant): DataResponse {
|
||||
$options = [
|
||||
'sessionId' => $participant->getSession()->getSessionId(),
|
||||
];
|
||||
|
||||
$proxy = $this->proxy->post(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v4/room/' . $room->getRemoteToken() . '/federation/active',
|
||||
$options,
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if (!in_array($statusCode, [Http::STATUS_OK, Http::STATUS_NOT_FOUND], true)) {
|
||||
$this->proxy->logUnexpectedStatusCode(__METHOD__, $proxy->getStatusCode());
|
||||
throw new CannotReachRemoteException();
|
||||
}
|
||||
|
||||
$headers = ['X-Nextcloud-Talk-Proxy-Hash' => $this->proxy->overwrittenRemoteTalkHash($proxy->getHeader('X-Nextcloud-Talk-Hash'))];
|
||||
|
||||
/** @var TalkRoom[] $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
|
||||
$data = $this->userConverter->convertAttendee($room, $data, 'actorType', 'actorId', 'displayName');
|
||||
|
||||
return new DataResponse($data, $statusCode, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\RoomController::leaveFederatedRoom()
|
||||
*
|
||||
* @param Room $room the federated room to leave
|
||||
* @param Participant $participant the federated user that will leave the
|
||||
* room; the participant must have a session
|
||||
* @return DataResponse<Http::STATUS_OK, array<empty>, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Federated user left the room
|
||||
*/
|
||||
public function leaveFederatedRoom(Room $room, Participant $participant): DataResponse {
|
||||
$options = [
|
||||
'sessionId' => $participant->getSession()->getSessionId(),
|
||||
];
|
||||
|
||||
$proxy = $this->proxy->delete(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v4/room/' . $room->getRemoteToken() . '/federation/active',
|
||||
$options,
|
||||
);
|
||||
|
||||
// STATUS_NOT_FOUND is not taken into account, as it should happen only
|
||||
// for non-federation requests.
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if (!in_array($statusCode, [Http::STATUS_OK], true)) {
|
||||
$this->proxy->logUnexpectedStatusCode(__METHOD__, $proxy->getStatusCode());
|
||||
throw new CannotReachRemoteException();
|
||||
}
|
||||
|
||||
return new DataResponse([], $statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\RoomController::getCapabilities()
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, TalkCapabilities|array<empty>, array{X-Nextcloud-Talk-Hash: string}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Get capabilities successfully
|
||||
*/
|
||||
public function getCapabilities(Room $room, Participant $participant): DataResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v4/room/' . $room->getRemoteToken() . '/capabilities',
|
||||
);
|
||||
|
||||
/** @var TalkCapabilities|array<empty> $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
|
||||
$headers = [
|
||||
'X-Nextcloud-Talk-Hash' => $this->proxy->overwrittenRemoteTalkHash($proxy->getHeader('X-Nextcloud-Talk-Hash')),
|
||||
];
|
||||
|
||||
return new DataResponse($data, Http::STATUS_OK, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|\stdClass
|
||||
*/
|
||||
protected function emptyArray(): array|\stdClass {
|
||||
// Cheating here to make sure the array is always a
|
||||
// JSON object on the API, even when there is no entry at all.
|
||||
return new \stdClass();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Federation\Proxy\TalkV1\Controller;
|
||||
|
||||
use OCA\Talk\Exceptions\CannotReachRemoteException;
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\ProxyRequest;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCA\Talk\Room;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
|
||||
/**
|
||||
* @psalm-import-type TalkSignalingSettings from ResponseDefinitions
|
||||
*/
|
||||
class SignalingController {
|
||||
public function __construct(
|
||||
protected ProxyRequest $proxy,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\SignalingController::getSettings()
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, TalkSignalingSettings, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array<empty>, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Signaling settings returned
|
||||
* 404: Room not found
|
||||
*/
|
||||
public function getSettings(Room $room, Participant $participant): DataResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v3/signaling/settings',
|
||||
[
|
||||
'token' => $room->getRemoteToken(),
|
||||
],
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if (!in_array($statusCode, [Http::STATUS_OK, Http::STATUS_NOT_FOUND], true)) {
|
||||
$this->proxy->logUnexpectedStatusCode(__METHOD__, $proxy->getStatusCode());
|
||||
throw new CannotReachRemoteException();
|
||||
}
|
||||
|
||||
/** @var TalkSignalingSettings|array<empty> $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
|
||||
return new DataResponse($data, $statusCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Federation\Proxy\TalkV1\Controller;
|
||||
|
||||
use OCA\Talk\Chat\Notifier;
|
||||
use OCA\Talk\Exceptions\CannotReachRemoteException;
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\ProxyRequest;
|
||||
use OCA\Talk\Federation\Proxy\TalkV1\UserConverter;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCA\Talk\Room;
|
||||
use OCA\Talk\Service\ParticipantService;
|
||||
use OCA\Talk\Service\RoomFormatter;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\ICacheFactory;
|
||||
|
||||
/**
|
||||
* @psalm-import-type TalkThreadInfo from ResponseDefinitions
|
||||
* @psalm-import-type TalkRoom from ResponseDefinitions
|
||||
*/
|
||||
class ThreadController {
|
||||
|
||||
public function __construct(
|
||||
protected ProxyRequest $proxy,
|
||||
protected UserConverter $userConverter,
|
||||
protected ParticipantService $participantService,
|
||||
protected RoomFormatter $roomFormatter,
|
||||
protected Notifier $notifier,
|
||||
ICacheFactory $cacheFactory,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\ThreadController::getRecentActiveThreads()
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, list<TalkThreadInfo>, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: List of threads returned
|
||||
*/
|
||||
public function getRecentActiveThreads(Room $room, Participant $participant, int $limit): DataResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/threads/recent',
|
||||
[
|
||||
'limit' => $limit,
|
||||
],
|
||||
);
|
||||
|
||||
/** @var list<TalkThreadInfo> $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
if (!empty($data)) {
|
||||
$data = $this->userConverter->convertThreadInfos($room, $data);
|
||||
}
|
||||
|
||||
return new DataResponse($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\ThreadController::getThread()
|
||||
*
|
||||
* @psalm-param non-negative-int $threadId
|
||||
* @return DataResponse<Http::STATUS_OK, TalkThreadInfo, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{error: 'thread'|'status'}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Thread info returned
|
||||
* 404: Thread not found
|
||||
*/
|
||||
public function getThread(Room $room, Participant $participant, int $threadId): DataResponse {
|
||||
$proxy = $this->proxy->get(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/threads/' . $threadId,
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if ($statusCode !== Http::STATUS_OK) {
|
||||
if ($statusCode !== Http::STATUS_NOT_FOUND) {
|
||||
$this->proxy->logUnexpectedStatusCode(__METHOD__, $statusCode);
|
||||
$data = ['error' => 'status'];
|
||||
} else {
|
||||
/** @var array{error: 'thread'} $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [Http::STATUS_NOT_FOUND]);
|
||||
}
|
||||
return new DataResponse($data, Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
/** @var TalkThreadInfo $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
$data = $this->userConverter->convertThreadInfo($room, $data);
|
||||
|
||||
return new DataResponse($data, Http::STATUS_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\ThreadController::setNotificationLevel()
|
||||
*
|
||||
* @psalm-param non-negative-int $threadId
|
||||
* @return DataResponse<Http::STATUS_OK, TalkThreadInfo, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{error: 'title'}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{error: 'thread'}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Thread renamed successfully
|
||||
* 400: When the provided title is empty
|
||||
* 404: Thread not found
|
||||
*/
|
||||
public function renameThread(Room $room, Participant $participant, int $threadId, string $threadTitle): DataResponse {
|
||||
$proxy = $this->proxy->put(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/threads/' . $threadId,
|
||||
['threadTitle' => $threadTitle],
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if ($statusCode !== Http::STATUS_OK) {
|
||||
if (!in_array($statusCode, [
|
||||
Http::STATUS_BAD_REQUEST,
|
||||
Http::STATUS_NOT_FOUND,
|
||||
], true)) {
|
||||
$statusCode = $this->proxy->logUnexpectedStatusCode(__METHOD__, $statusCode);
|
||||
$data = ['error' => 'thread'];
|
||||
} elseif ($statusCode === Http::STATUS_BAD_REQUEST) {
|
||||
/** @var array{error: 'title'} $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [
|
||||
Http::STATUS_BAD_REQUEST,
|
||||
]);
|
||||
} else {
|
||||
/** @var array{error: 'thread'} $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [
|
||||
Http::STATUS_NOT_FOUND,
|
||||
]);
|
||||
}
|
||||
return new DataResponse($data, $statusCode);
|
||||
}
|
||||
|
||||
/** @var TalkThreadInfo $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
$data = $this->userConverter->convertThreadInfo($room, $data);
|
||||
|
||||
return new DataResponse($data, Http::STATUS_OK);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see \OCA\Talk\Controller\ThreadController::setNotificationLevel()
|
||||
*
|
||||
* @psalm-param non-negative-int $messageId
|
||||
* @psalm-param Participant::NOTIFY_* $level
|
||||
* @return DataResponse<Http::STATUS_OK, TalkThreadInfo, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_NOT_FOUND, array{error: 'level'|'message'|'status'|'top-most'}, array{}>
|
||||
* @throws CannotReachRemoteException
|
||||
*
|
||||
* 200: Successfully set notification level for thread
|
||||
* 400: Notification level was invalid
|
||||
* 404: Message or top most message not found
|
||||
*/
|
||||
public function setNotificationLevel(Room $room, Participant $participant, int $messageId, int $level): DataResponse {
|
||||
$proxy = $this->proxy->post(
|
||||
$participant->getAttendee()->getInvitedCloudId(),
|
||||
$participant->getAttendee()->getAccessToken(),
|
||||
$room->getRemoteServer() . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $room->getRemoteToken() . '/threads/' . $messageId . '/notify',
|
||||
['level' => $level],
|
||||
);
|
||||
|
||||
$statusCode = $proxy->getStatusCode();
|
||||
if ($statusCode !== Http::STATUS_OK) {
|
||||
if (!in_array($statusCode, [
|
||||
Http::STATUS_BAD_REQUEST,
|
||||
Http::STATUS_NOT_FOUND,
|
||||
], true)) {
|
||||
$statusCode = $this->proxy->logUnexpectedStatusCode(__METHOD__, $statusCode);
|
||||
$data = ['error' => 'status'];
|
||||
} else {
|
||||
/** @var array{error: 'level'|'message'|'top-most'} $data */
|
||||
$data = $this->proxy->getOCSData($proxy, [
|
||||
Http::STATUS_BAD_REQUEST,
|
||||
Http::STATUS_NOT_FOUND,
|
||||
]);
|
||||
}
|
||||
return new DataResponse($data, $statusCode);
|
||||
}
|
||||
|
||||
/** @var TalkThreadInfo $data */
|
||||
$data = $this->proxy->getOCSData($proxy);
|
||||
$data = $this->userConverter->convertThreadInfo($room, $data);
|
||||
|
||||
return new DataResponse($data, Http::STATUS_OK);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user