summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoas Schilling <213943+nickvergessen@users.noreply.github.com>2024-03-28 17:09:29 +0100
committerGitHub <noreply@github.com>2024-03-28 17:09:29 +0100
commit5233c4d3c0e288160aeb70a2be583428733cb446 (patch)
tree47548ba7c3da93cd3bb59ea357f6c7fab570b41a
parent61a6b981c5de06ebdc799d93a6a2aeaed0a83619 (diff)
parent6962f7857a8147a4e74c4fecd16f1a4b12a7ae5a (diff)
Merge pull request #11971 from nextcloud/backport/11944/stable29
[stable29] feat(federation): Expose `X-Nextcloud-Talk-Federation-Invites` header with the pending invitations on roo…
-rw-r--r--docs/conversation.md9
-rw-r--r--lib/BackgroundJob/ResetAssignedSignalingServer.php3
-rw-r--r--lib/CachePrefix.php36
-rw-r--r--lib/Chat/ChatManager.php5
-rw-r--r--lib/Controller/FederationController.php1
-rw-r--r--lib/Controller/RoomController.php19
-rw-r--r--lib/Federation/CloudFederationProviderTalk.php3
-rw-r--r--lib/Federation/FederationManager.php4
-rw-r--r--lib/Federation/Proxy/TalkV1/Controller/ChatController.php3
-rw-r--r--lib/Model/InvitationMapper.php21
-rw-r--r--lib/Service/ParticipantService.php5
-rw-r--r--lib/Signaling/Manager.php3
-rw-r--r--openapi-full.json5
-rw-r--r--openapi.json5
-rw-r--r--src/types/openapi/openapi-full.ts1
-rw-r--r--src/types/openapi/openapi.ts1
-rw-r--r--tests/integration/features/bootstrap/FeatureContext.php14
-rw-r--r--tests/integration/features/federation/invite.feature7
18 files changed, 129 insertions, 16 deletions
diff --git a/docs/conversation.md b/docs/conversation.md
index 65a38c21b..9b9fe58b5 100644
--- a/docs/conversation.md
+++ b/docs/conversation.md
@@ -40,10 +40,11 @@
- Header:
-| field | type | Description |
-|------------------------------------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `X-Nextcloud-Talk-Hash` | string | Sha1 value over some config. When you receive a different value on subsequent requests, the capabilities and the signaling settings should be refreshed. |
-| `X-Nextcloud-Talk-Modified-Before` | string | Timestamp from before the database request that can be used as `modifiedSince` parameter in the next request |
+| field | type | Description |
+|---------------------------------------|--------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `X-Nextcloud-Talk-Hash` | string | Sha1 value over some config. When you receive a different value on subsequent requests, the capabilities and the signaling settings should be refreshed. |
+| `X-Nextcloud-Talk-Modified-Before` | string | Timestamp from before the database request that can be used as `modifiedSince` parameter in the next request |
+| `X-Nextcloud-Talk-Federation-Invites` | string | *Optional:* Number of pending invites to federated conversations the user has. (Only available when the user can do federation and has at least one invite pending) |
- Data:
Array of conversations, each conversation has at least:
diff --git a/lib/BackgroundJob/ResetAssignedSignalingServer.php b/lib/BackgroundJob/ResetAssignedSignalingServer.php
index 49df8df85..9d819f62f 100644
--- a/lib/BackgroundJob/ResetAssignedSignalingServer.php
+++ b/lib/BackgroundJob/ResetAssignedSignalingServer.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace OCA\Talk\BackgroundJob;
+use OCA\Talk\CachePrefix;
use OCA\Talk\Manager;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJob;
@@ -49,7 +50,7 @@ class ResetAssignedSignalingServer extends TimedJob {
$this->setInterval(60 * 5);
$this->setTimeSensitivity(IJob::TIME_SENSITIVE);
- $this->cache = $cacheFactory->createDistributed('hpb_servers');
+ $this->cache = $cacheFactory->createDistributed(CachePrefix::SIGNALING_ASSIGNED_SERVER);
}
protected function run($argument): void {
diff --git a/lib/CachePrefix.php b/lib/CachePrefix.php
new file mode 100644
index 000000000..4b3c13c47
--- /dev/null
+++ b/lib/CachePrefix.php
@@ -0,0 +1,36 @@
+<?php
+/*
+ * @copyright Copyright (c) 2024 Joas Schilling <coding@schilljs.com>
+ */
+
+declare(strict_types=1);
+/*
+ * @copyright Copyright (c) 2024 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Talk;
+
+class CachePrefix {
+ public const FEDERATED_PCM = 'talk/pcm/';
+ public const CHAT_LAST_MESSAGE_ID = 'talk/lastmsgid';
+ public const CHAT_UNREAD_COUNT = 'talk/unreadcount';
+ public const SIGNALING_ASSIGNED_SERVER = 'hpb_servers';
+}
diff --git a/lib/Chat/ChatManager.php b/lib/Chat/ChatManager.php
index 7988aa9f3..e6d1cf5b9 100644
--- a/lib/Chat/ChatManager.php
+++ b/lib/Chat/ChatManager.php
@@ -27,6 +27,7 @@ namespace OCA\Talk\Chat;
use DateInterval;
use OC\Memcache\ArrayCache;
use OC\Memcache\NullCache;
+use OCA\Talk\CachePrefix;
use OCA\Talk\Events\BeforeChatMessageSentEvent;
use OCA\Talk\Events\BeforeSystemMessageSentEvent;
use OCA\Talk\Events\ChatMessageSentEvent;
@@ -113,8 +114,8 @@ class ChatManager {
protected IRequest $request,
protected LoggerInterface $logger,
) {
- $this->cache = $cacheFactory->createDistributed('talk/lastmsgid');
- $this->unreadCountCache = $cacheFactory->createDistributed('talk/unreadcount');
+ $this->cache = $cacheFactory->createDistributed(CachePrefix::CHAT_LAST_MESSAGE_ID);
+ $this->unreadCountCache = $cacheFactory->createDistributed(CachePrefix::CHAT_UNREAD_COUNT);
}
/**
diff --git a/lib/Controller/FederationController.php b/lib/Controller/FederationController.php
index a9b352604..aea6e8a7e 100644
--- a/lib/Controller/FederationController.php
+++ b/lib/Controller/FederationController.php
@@ -51,7 +51,6 @@ use OCP\IUserSession;
* @psalm-import-type TalkRoom from ResponseDefinitions
*/
class FederationController extends OCSController {
-
public function __construct(
IRequest $request,
private FederationManager $federationManager,
diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php
index 3f5cbaa53..b6df32698 100644
--- a/lib/Controller/RoomController.php
+++ b/lib/Controller/RoomController.php
@@ -181,7 +181,7 @@ class RoomController extends AEnvironmentAwareController {
* @param bool $includeStatus Include the user status
* @param int $modifiedSince Filter rooms modified after a timestamp
* @psalm-param non-negative-int $modifiedSince
- * @return DataResponse<Http::STATUS_OK, TalkRoom[], array{X-Nextcloud-Talk-Hash: string, X-Nextcloud-Talk-Modified-Before: numeric-string}>
+ * @return DataResponse<Http::STATUS_OK, TalkRoom[], array{X-Nextcloud-Talk-Hash: string, X-Nextcloud-Talk-Modified-Before: numeric-string, X-Nextcloud-Talk-Federation-Invites?: numeric-string}>
*
* 200: Return list of rooms
*/
@@ -191,6 +191,7 @@ class RoomController extends AEnvironmentAwareController {
$event = new BeforeRoomsFetchEvent($this->userId);
$this->dispatcher->dispatchTyped($event);
+ $user = $this->userManager->get($this->userId);
if ($noStatusUpdate === 0) {
$isMobileApp = $this->request->isUserAgent([
@@ -201,7 +202,7 @@ class RoomController extends AEnvironmentAwareController {
if ($isMobileApp) {
// Bump the user status again
$event = new UserLiveStatusEvent(
- $this->userManager->get($this->userId),
+ $user,
IUserStatus::ONLINE,
$this->timeFactory->getTime()
);
@@ -255,7 +256,19 @@ class RoomController extends AEnvironmentAwareController {
}
}
- return new DataResponse($return, Http::STATUS_OK, array_merge($this->getTalkHashHeader(), ['X-Nextcloud-Talk-Modified-Before' => (string) $nextModifiedSince]));
+ /** @var array{X-Nextcloud-Talk-Modified-Before: numeric-string, X-Nextcloud-Talk-Federation-Invites?: numeric-string} $headers */
+ $headers = ['X-Nextcloud-Talk-Modified-Before' => (string) $nextModifiedSince];
+ if ($this->talkConfig->isFederationEnabledForUserId($user)) {
+ $numInvites = $this->federationManager->getNumberOfPendingInvitationsForUser($user);
+ if ($numInvites !== 0) {
+ $headers['X-Nextcloud-Talk-Federation-Invites'] = (string) $numInvites;
+ }
+ }
+
+ /** @var array{X-Nextcloud-Talk-Hash: string, X-Nextcloud-Talk-Modified-Before: numeric-string, X-Nextcloud-Talk-Federation-Invites?: numeric-string} $headers */
+ $headers = array_merge($this->getTalkHashHeader(), $headers);
+
+ return new DataResponse($return, Http::STATUS_OK, $headers);
}
/**
diff --git a/lib/Federation/CloudFederationProviderTalk.php b/lib/Federation/CloudFederationProviderTalk.php
index 5c9e2c824..20bb51f38 100644
--- a/lib/Federation/CloudFederationProviderTalk.php
+++ b/lib/Federation/CloudFederationProviderTalk.php
@@ -28,6 +28,7 @@ namespace OCA\Talk\Federation;
use Exception;
use OCA\FederatedFileSharing\AddressHandler;
use OCA\Talk\AppInfo\Application;
+use OCA\Talk\CachePrefix;
use OCA\Talk\Config;
use OCA\Talk\Events\AAttendeeRemovedEvent;
use OCA\Talk\Events\ARoomModifiedEvent;
@@ -97,7 +98,7 @@ class CloudFederationProviderTalk implements ICloudFederationProvider {
private UserConverter $userConverter,
ICacheFactory $cacheFactory,
) {
- $this->proxyCacheMessages = $cacheFactory->isAvailable() ? $cacheFactory->createDistributed('talk/pcm/') : null;
+ $this->proxyCacheMessages = $cacheFactory->isAvailable() ? $cacheFactory->createDistributed(CachePrefix::FEDERATED_PCM) : null;
}
/**
diff --git a/lib/Federation/FederationManager.php b/lib/Federation/FederationManager.php
index a45c6c8cd..6965443fc 100644
--- a/lib/Federation/FederationManager.php
+++ b/lib/Federation/FederationManager.php
@@ -257,6 +257,10 @@ class FederationManager {
return $this->invitationMapper->getInvitationsForUser($user);
}
+ public function getNumberOfPendingInvitationsForUser(IUser $user): int {
+ return $this->invitationMapper->countInvitationsForUser($user, Invitation::STATE_PENDING);
+ }
+
public function getNumberOfInvitations(Room $room): int {
return $this->invitationMapper->countInvitationsForLocalRoom($room);
}
diff --git a/lib/Federation/Proxy/TalkV1/Controller/ChatController.php b/lib/Federation/Proxy/TalkV1/Controller/ChatController.php
index e926e5907..44a20ccf5 100644
--- a/lib/Federation/Proxy/TalkV1/Controller/ChatController.php
+++ b/lib/Federation/Proxy/TalkV1/Controller/ChatController.php
@@ -26,6 +26,7 @@ declare(strict_types=1);
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;
@@ -57,7 +58,7 @@ class ChatController {
protected Notifier $notifier,
ICacheFactory $cacheFactory,
) {
- $this->proxyCacheMessages = $cacheFactory->isAvailable() ? $cacheFactory->createDistributed('talk/pcm/') : null;
+ $this->proxyCacheMessages = $cacheFactory->isAvailable() ? $cacheFactory->createDistributed(CachePrefix::FEDERATED_PCM) : null;
}
/**
diff --git a/lib/Model/InvitationMapper.php b/lib/Model/InvitationMapper.php
index 1c676aa45..e54b9bd4a 100644
--- a/lib/Model/InvitationMapper.php
+++ b/lib/Model/InvitationMapper.php
@@ -96,6 +96,27 @@ class InvitationMapper extends QBMapper {
}
/**
+ * @psalm-param Invitation::STATE_*|null $state
+ */
+ public function countInvitationsForUser(IUser $user, ?int $state = null): int {
+ $qb = $this->db->getQueryBuilder();
+
+ $qb->select($qb->func()->count('*'))
+ ->from($this->getTableName())
+ ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($user->getUID())));
+
+ if ($state !== null) {
+ $qb->andWhere($qb->expr()->eq('state', $qb->createNamedParameter($state)));
+ }
+
+ $result = $qb->executeQuery();
+ $count = (int) $result->fetchOne();
+ $result->closeCursor();
+
+ return $count;
+ }
+
+ /**
* @throws DoesNotExistException
*/
public function getInvitationForUserByLocalRoom(Room $room, string $userId, bool $caseInsensitive = false): Invitation {
diff --git a/lib/Service/ParticipantService.php b/lib/Service/ParticipantService.php
index 31c31a664..ed4c6a888 100644
--- a/lib/Service/ParticipantService.php
+++ b/lib/Service/ParticipantService.php
@@ -26,6 +26,7 @@ namespace OCA\Talk\Service;
use OCA\Circles\CirclesManager;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\Member;
+use OCA\Talk\CachePrefix;
use OCA\Talk\Config;
use OCA\Talk\Events\AAttendeeRemovedEvent;
use OCA\Talk\Events\AParticipantModifiedEvent;
@@ -562,9 +563,9 @@ class ParticipantService {
$roomService = Server::get(RoomService::class);
$roomService->setLastMessage($room, $message);
- $lastMessageCache = $this->cacheFactory->createDistributed('talk/lastmsgid');
+ $lastMessageCache = $this->cacheFactory->createDistributed(CachePrefix::CHAT_LAST_MESSAGE_ID);
$lastMessageCache->remove($room->getToken());
- $unreadCountCache = $this->cacheFactory->createDistributed('talk/unreadcount');
+ $unreadCountCache = $this->cacheFactory->createDistributed(CachePrefix::CHAT_UNREAD_COUNT);
$unreadCountCache->clear($room->getId() . '-');
$event = new SystemMessagesMultipleSentEvent($room, $message);
diff --git a/lib/Signaling/Manager.php b/lib/Signaling/Manager.php
index 472543c25..8736c26d7 100644
--- a/lib/Signaling/Manager.php
+++ b/lib/Signaling/Manager.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace OCA\Talk\Signaling;
+use OCA\Talk\CachePrefix;
use OCA\Talk\Config;
use OCA\Talk\Room;
use OCA\Talk\Service\RoomService;
@@ -42,7 +43,7 @@ class Manager {
protected RoomService $roomService,
ICacheFactory $cacheFactory,
) {
- $this->cache = $cacheFactory->createDistributed('hpb_servers');
+ $this->cache = $cacheFactory->createDistributed(CachePrefix::SIGNALING_ASSIGNED_SERVER);
}
public function isCompatibleSignalingServer(IResponse $response): bool {
diff --git a/openapi-full.json b/openapi-full.json
index e519deb7d..a46ecd253 100644
--- a/openapi-full.json
+++ b/openapi-full.json
@@ -9560,6 +9560,11 @@
"schema": {
"type": "string"
}
+ },
+ "X-Nextcloud-Talk-Federation-Invites": {
+ "schema": {
+ "type": "string"
+ }
}
},
"content": {
diff --git a/openapi.json b/openapi.json
index f6e203b2d..a352fd839 100644
--- a/openapi.json
+++ b/openapi.json
@@ -9447,6 +9447,11 @@
"schema": {
"type": "string"
}
+ },
+ "X-Nextcloud-Talk-Federation-Invites": {
+ "schema": {
+ "type": "string"
+ }
}
},
"content": {
diff --git a/src/types/openapi/openapi-full.ts b/src/types/openapi/openapi-full.ts
index d192baa93..516416269 100644
--- a/src/types/openapi/openapi-full.ts
+++ b/src/types/openapi/openapi-full.ts
@@ -3640,6 +3640,7 @@ export type operations = {
headers: {
"X-Nextcloud-Talk-Hash"?: string;
"X-Nextcloud-Talk-Modified-Before"?: string;
+ "X-Nextcloud-Talk-Federation-Invites"?: string;
};
content: {
"application/json": {
diff --git a/src/types/openapi/openapi.ts b/src/types/openapi/openapi.ts
index 8e09440d1..4a6845fe3 100644
--- a/src/types/openapi/openapi.ts
+++ b/src/types/openapi/openapi.ts
@@ -3463,6 +3463,7 @@ export type operations = {
headers: {
"X-Nextcloud-Talk-Hash"?: string;
"X-Nextcloud-Talk-Modified-Before"?: string;
+ "X-Nextcloud-Talk-Federation-Invites"?: string;
};
content: {
"application/json": {
diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php
index cc8ac557e..d8a18dd29 100644
--- a/tests/integration/features/bootstrap/FeatureContext.php
+++ b/tests/integration/features/bootstrap/FeatureContext.php
@@ -3027,6 +3027,20 @@ class FeatureContext implements Context, SnippetAcceptingContext {
}
/**
+ * @Then /^last response has federation invites header set to "([^"]*)"$/
+ *
+ * @param string $count
+ */
+ public function hasFederationInvitesHeader(string $count): void {
+ if ($count === 'NULL') {
+ Assert::assertFalse($this->response->hasHeader('X-Nextcloud-Talk-Federation-Invites'), "Should not contain 'X-Nextcloud-Talk-Federation-Invites' header\n" . json_encode($this->response->getHeaders(), JSON_PRETTY_PRINT));
+ } else {
+ Assert::assertTrue($this->response->hasHeader('X-Nextcloud-Talk-Federation-Invites'), "Should contain 'X-Nextcloud-Talk-Federation-Invites' header\n" . json_encode($this->response->getHeaders(), JSON_PRETTY_PRINT));
+ Assert::assertEquals($count, $this->response->getHeader('X-Nextcloud-Talk-Federation-Invites')[0]);
+ }
+ }
+
+ /**
* @Then /^user "([^"]*)" creates (\d+) (automatic|manual|free) breakout rooms for "([^"]*)" with (\d+) \((v1)\)$/
*
* @param string $user
diff --git a/tests/integration/features/federation/invite.feature b/tests/integration/features/federation/invite.feature
index 938873643..43308d2cf 100644
--- a/tests/integration/features/federation/invite.feature
+++ b/tests/integration/features/federation/invite.feature
@@ -41,6 +41,9 @@ Feature: federation/invite
| room | users | participant1 | federated_user_added | You invited {federated_user} | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"},"federated_user":{"type":"user","id":"participant2","name":"participant2@localhost:8180","server":"http:\/\/localhost:8180"}} |
| room | users | participant1 | conversation_created | You created the conversation | {"actor":{"type":"user","id":"participant1","name":"participant1-displayname"}} |
And user "participant1" adds federated_user "participant2" to room "room" with 200 (v4)
+ Then user "participant2" is participant of the following rooms (v4)
+ | id | name | type |
+ Then last response has federation invites header set to "1"
When user "participant1" sees the following attendees in room "room" with 200 (v4)
| actorType | actorId | participantType |
| users | participant1 | 1 |
@@ -59,6 +62,10 @@ Feature: federation/invite
And user "participant2" accepts invite to room "room" of server "LOCAL" with 200 (v1)
| id | name | type | remoteServer | remoteToken |
| room | room | 3 | LOCAL | room |
+ Then user "participant2" is participant of the following rooms (v4)
+ | id | name | type | remoteServer | remoteToken |
+ | room | room | 3 | LOCAL | room |
+ Then last response has federation invites header set to "NULL"
And user "participant2" accepts invite to room "room" of server "LOCAL" with 400 (v1)
| error | state |
And user "participant2" declines invite to room "room" of server "LOCAL" with 400 (v1)