diff --git a/appinfo/info.xml b/appinfo/info.xml
index e88217fe344..5ae2f875bf8 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -18,7 +18,7 @@
* 🌉 **Sync with other chat solutions** With [Matterbridge](https://github.com/42wim/matterbridge/) being integrated in Talk, you can easily sync a lot of other chat solutions to Nextcloud Talk and vice-versa.
]]>
- 21.0.0-dev.2
+ 21.0.0-dev.3
agpl
Anna Larch
@@ -66,6 +66,7 @@
OCA\Talk\BackgroundJob\CheckTurnCertificate
OCA\Talk\BackgroundJob\ExpireChatMessages
OCA\Talk\BackgroundJob\ExpireSignalingMessage
+ OCA\Talk\BackgroundJob\LockInactiveRooms
OCA\Talk\BackgroundJob\MaximumCallDuration
OCA\Talk\BackgroundJob\Reminder
OCA\Talk\BackgroundJob\RemoveEmptyRooms
diff --git a/docs/settings.md b/docs/settings.md
index dc2cfe836c1..b828fe2cf33 100644
--- a/docs/settings.md
+++ b/docs/settings.md
@@ -113,3 +113,5 @@ Legend:
| `conversations_files` | string
`1` or `0` | `1` | No | 🖌️ | Whether the files app integration is enabled allowing to start conversations in the right sidebar |
| `conversations_files_public_shares` | string
`1` or `0` | `1` | No | 🖌️ | Whether the public share integration is enabled allowing to start conversations in the right sidebar on the public share page (Requires `conversations_files` also to be enabled) |
| `enable_matterbridge` | string
`1` or `0` | `0` | No | 🖌️ | Whether the Matterbridge integration is enabled and can be configured |
+| `inactivity_lock_after_days` | int | `0` | No | | A duration (in days) after which rooms are locked. Calculated from the last activity in the room. |
+| `inactivity_enable_lobby` | string
`1` or `0` | `0` | No | | Additionally enable the lobby for inactive rooms so they can only be read by moderators. |
diff --git a/lib/BackgroundJob/LockInactiveRooms.php b/lib/BackgroundJob/LockInactiveRooms.php
new file mode 100644
index 00000000000..ae30df4c306
--- /dev/null
+++ b/lib/BackgroundJob/LockInactiveRooms.php
@@ -0,0 +1,56 @@
+setInterval(60 * 60 * 24);
+ $this->setTimeSensitivity(IJob::TIME_SENSITIVE);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function run($argument): void {
+ $interval = $this->appConfig->getInactiveLockTime();
+ $forceLobby = $this->appConfig->enableLobbyOnLockedRooms();
+ if ($interval === 0) {
+ return;
+ }
+ $timestamp = $this->time->getTime() - $interval * 60 * 60 * 24;
+ $time = $this->time->getDateTime('@' . $timestamp);
+ $rooms = $this->roomService->getInactiveRooms($time);
+ array_map(function (Room $room) use ($forceLobby) {
+ $this->roomService->setReadOnly($room, Room::READ_ONLY);
+ $this->logger->debug("Locking room {$room->getId()} due to inactivity");
+ if ($forceLobby) {
+ $this->roomService->setLobby($room, Webinary::LOBBY_NON_MODERATORS, $this->time->getDateTime());
+ $this->logger->debug("Enabling lobby for room {$room->getId()}");
+ }
+ }, $rooms);
+ }
+}
diff --git a/lib/Config.php b/lib/Config.php
index 87f26c52cd5..ccf88382bc2 100644
--- a/lib/Config.php
+++ b/lib/Config.php
@@ -694,4 +694,15 @@ public function getBlurVirtualBackground(?string $userId): bool {
}
return false;
}
+
+ /**
+ * User setting falling back to admin defined app config
+ */
+ public function getInactiveLockTime(): int {
+ return $this->appConfig->getAppValueInt('inactivity_lock_after_days');
+ }
+
+ public function enableLobbyOnLockedRooms(): bool {
+ return $this->appConfig->getAppValueBool('inactivity_enable_lobby');
+ }
}
diff --git a/lib/Manager.php b/lib/Manager.php
index a0b4c7fe111..28e311a6887 100644
--- a/lib/Manager.php
+++ b/lib/Manager.php
@@ -317,6 +317,30 @@ public function getRoomsLongerActiveSince(\DateTime $maxActiveSince): array {
return $rooms;
}
+ /**
+ * @return list
+ */
+ public function getInactiveRooms(\DateTime $inactiveSince): array {
+ $query = $this->db->getQueryBuilder();
+ $helper = new SelectHelper();
+ $helper->selectRoomsTable($query);
+ $query->from('talk_rooms', 'r')
+ ->andWhere($query->expr()->lte('r.last_activity', $query->createNamedParameter($inactiveSince, IQueryBuilder::PARAM_DATETIME_MUTABLE)))
+ ->andWhere($query->expr()->neq('r.read_only', $query->createNamedParameter(Room::READ_ONLY, IQueryBuilder::PARAM_INT)))
+ ->andWhere($query->expr()->in('r.type', $query->createNamedParameter([Room::TYPE_PUBLIC, Room::TYPE_GROUP], IQueryBuilder::PARAM_INT_ARRAY)))
+ ->andWhere($query->expr()->emptyString('remoteServer'))
+ ->orderBy('r.id', 'ASC');
+ $result = $query->executeQuery();
+
+ $rooms = [];
+ while ($row = $result->fetch()) {
+ $rooms[] = $this->createRoomObject($row);
+ }
+ $result->closeCursor();
+
+ return $rooms;
+ }
+
/**
* @param string $userId
* @param array $sessionIds A list of talk sessions to consider for loading (otherwise no session is loaded)
diff --git a/lib/Service/RoomService.php b/lib/Service/RoomService.php
index bba48b4a67d..1ccd4f06a04 100644
--- a/lib/Service/RoomService.php
+++ b/lib/Service/RoomService.php
@@ -1214,4 +1214,11 @@ public function deleteRoom(Room $room): void {
));
}
}
+
+ /**
+ * @return list
+ */
+ public function getInactiveRooms(\DateTime $inactiveSince): array {
+ return $this->manager->getInactiveRooms($inactiveSince);
+ }
}
diff --git a/tests/php/BackgroundJob/LockInactiveRoomsTest.php b/tests/php/BackgroundJob/LockInactiveRoomsTest.php
new file mode 100644
index 00000000000..4a3662af5ee
--- /dev/null
+++ b/tests/php/BackgroundJob/LockInactiveRoomsTest.php
@@ -0,0 +1,153 @@
+timeFactory = $this->createMock(ITimeFactory::class);
+ $this->roomService = $this->createMock(RoomService::class);
+ $this->appConfig = $this->createMock(Config::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+ $this->job = new LockInactiveRooms($this->timeFactory,
+ $this->roomService,
+ $this->appConfig,
+ $this->logger
+ );
+ }
+
+ public function testNotEnabled(): void {
+ $this->appConfig->expects(self::once())
+ ->method('getInactiveLockTime')
+ ->willReturn(0);
+ $this->appConfig->expects(self::once())
+ ->method('enableLobbyOnLockedRooms')
+ ->willReturn(false);
+ $this->timeFactory->expects(self::never())
+ ->method(self::anything());
+ $this->roomService->expects(self::never())
+ ->method(self::anything());
+ $this->logger->expects(self::never())
+ ->method(self::anything());
+
+ $this->job->run('t');
+ }
+
+ public function testNoRooms(): void {
+ $this->appConfig->expects(self::once())
+ ->method('getInactiveLockTime')
+ ->willReturn(123);
+ $this->appConfig->expects(self::once())
+ ->method('enableLobbyOnLockedRooms')
+ ->willReturn(false);
+ $this->timeFactory->expects(self::once())
+ ->method('getTime');
+ $this->timeFactory->expects(self::once())
+ ->method('getDateTime');
+ $this->roomService->expects(self::once())
+ ->method('getInactiveRooms')
+ ->willReturn([]);
+ $this->roomService->expects(self::never())
+ ->method('setReadOnly');
+ $this->roomService->expects(self::never())
+ ->method('setLobby');
+ $this->logger->expects(self::never())
+ ->method(self::anything());
+
+ $this->job->run('t');
+
+ }
+
+ public function testLockRooms(): void {
+ $rooms = [
+ $this->createConfiguredMock(Room::class, [
+ 'getReadOnly' => 0,
+ 'getType' => Room::TYPE_PUBLIC,
+ ]),
+ $this->createConfiguredMock(Room::class, [
+ 'getReadOnly' => 0,
+ 'getType' => Room::TYPE_GROUP,
+ ]),
+ ];
+
+ $this->appConfig->expects(self::once())
+ ->method('getInactiveLockTime')
+ ->willReturn(123);
+ $this->appConfig->expects(self::once())
+ ->method('enableLobbyOnLockedRooms')
+ ->willReturn(false);
+ $this->timeFactory->expects(self::once())
+ ->method('getTime');
+ $this->timeFactory->expects(self::once())
+ ->method('getDateTime');
+ $this->roomService->expects(self::once())
+ ->method('getInactiveRooms')
+ ->willReturn($rooms);
+ $this->roomService->expects(self::exactly(2))
+ ->method('setReadOnly');
+ $this->roomService->expects(self::never())
+ ->method('setLobby');
+ $this->logger->expects(self::exactly(2))
+ ->method('debug');
+
+ $this->job->run('t');
+
+ }
+
+ public function testLockRoomsAndEnableLobby(): void {
+ $rooms = [
+ $this->createConfiguredMock(Room::class, [
+ 'getReadOnly' => 0,
+ 'getType' => Room::TYPE_PUBLIC,
+ ]),
+ $this->createConfiguredMock(Room::class, [
+ 'getReadOnly' => 0,
+ 'getType' => Room::TYPE_GROUP,
+ ]),
+ ];
+
+ $this->appConfig->expects(self::once())
+ ->method('getInactiveLockTime')
+ ->willReturn(123);
+ $this->appConfig->expects(self::once())
+ ->method('enableLobbyOnLockedRooms')
+ ->willReturn(true);
+ $this->timeFactory->expects(self::once())
+ ->method('getTime');
+ $this->timeFactory->expects(self::any())
+ ->method('getDateTime');
+ $this->roomService->expects(self::once())
+ ->method('getInactiveRooms')
+ ->willReturn($rooms);
+ $this->roomService->expects(self::exactly(2))
+ ->method('setReadOnly');
+ $this->roomService->expects(self::exactly(2))
+ ->method('setLobby');
+ $this->logger->expects(self::exactly(4))
+ ->method('debug');
+
+ $this->job->run('t');
+ }
+}