Browse Source

feat(conversation): Add option to allow deleting one-to-one conversations

Signed-off-by: Joas Schilling <coding@schilljs.com>
pull/14064/head
Joas Schilling 11 months ago
parent
commit
00a8e4a3f1
No known key found for this signature in database GPG Key ID: F72FA5B49FFA96B0
  1. 1
      docs/settings.md
  2. 13
      lib/Controller/ChatController.php
  3. 13
      lib/Controller/RoomController.php
  4. 9
      lib/Service/RoomFormatter.php
  5. 10
      tests/integration/features/chat-1/delete.feature
  6. 32
      tests/integration/features/conversation-3/one-to-one.feature

1
docs/settings.md

@ -112,6 +112,7 @@ Legend:
| `federation_only_trusted_servers` | string<br>`1` or `0` | `0` | Yes | | 🏗️ *Work in progress:* Whether federation should be limited to the list of "Trusted servers" | | `federation_only_trusted_servers` | string<br>`1` or `0` | `0` | Yes | | 🏗️ *Work in progress:* Whether federation should be limited to the list of "Trusted servers" |
| `conversations_files` | string<br>`1` or `0` | `1` | No | 🖌️ | Whether the files app integration is enabled allowing to start conversations in the right sidebar | | `conversations_files` | string<br>`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<br>`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) | | `conversations_files_public_shares` | string<br>`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) |
| `delete_one_to_one_conversations` | string<br>`1` or `0` | `0` | No | ️ | Whether one-to-one conversations can be left by either participant or should be deleted when one participant leaves |
| `enable_matterbridge` | string<br>`1` or `0` | `0` | No | 🖌️ | Whether the Matterbridge integration is enabled and can be configured | | `enable_matterbridge` | string<br>`1` or `0` | `0` | No | 🖌️ | Whether the Matterbridge integration is enabled and can be configured |
| `force_passwords` | string<br>`1` or `0` | `0` | No | ️ | Whether public chats are forced to use a password | | `force_passwords` | string<br>`1` or `0` | `0` | No | ️ | Whether public chats are forced to use a password |
| `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_lock_after_days` | int | `0` | No | | A duration (in days) after which rooms are locked. Calculated from the last activity in the room. |

13
lib/Controller/ChatController.php

@ -1185,10 +1185,15 @@ class ChatController extends AEnvironmentAwareOCSController {
#[RequireReadWriteConversation] #[RequireReadWriteConversation]
public function clearHistory(): DataResponse { public function clearHistory(): DataResponse {
$attendee = $this->participant->getAttendee(); $attendee = $this->participant->getAttendee();
if (!$this->participant->hasModeratorPermissions(false)
|| $this->room->getType() === Room::TYPE_ONE_TO_ONE
|| $this->room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) {
// Actor is not a moderator or not the owner of the message
if (!$this->participant->hasModeratorPermissions(false)) {
// Actor is not a moderator
return new DataResponse(null, Http::STATUS_FORBIDDEN);
}
if (!$this->appConfig->getAppValueBool('delete_one_to_one_conversations')
&& ($this->room->getType() === Room::TYPE_ONE_TO_ONE
|| $this->room->getType() === Room::TYPE_ONE_TO_ONE_FORMER)) {
// Not allowed to purge one-to-one conversations
return new DataResponse(null, Http::STATUS_FORBIDDEN); return new DataResponse(null, Http::STATUS_FORBIDDEN);
} }

13
lib/Controller/RoomController.php

@ -70,6 +70,7 @@ use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\OpenAPI; use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Services\IAppConfig;
use OCP\AppFramework\Utility\ITimeFactory; use OCP\AppFramework\Utility\ITimeFactory;
use OCP\EventDispatcher\IEventDispatcher; use OCP\EventDispatcher\IEventDispatcher;
use OCP\Federation\ICloudIdManager; use OCP\Federation\ICloudIdManager;
@ -116,6 +117,7 @@ class RoomController extends AEnvironmentAwareOCSController {
protected ChecksumVerificationService $checksumVerificationService, protected ChecksumVerificationService $checksumVerificationService,
protected RoomFormatter $roomFormatter, protected RoomFormatter $roomFormatter,
protected IConfig $config, protected IConfig $config,
protected IAppConfig $appConfig,
protected Config $talkConfig, protected Config $talkConfig,
protected ICloudIdManager $cloudIdManager, protected ICloudIdManager $cloudIdManager,
protected IPhoneNumberUtil $phoneNumberUtil, protected IPhoneNumberUtil $phoneNumberUtil,
@ -857,7 +859,8 @@ class RoomController extends AEnvironmentAwareOCSController {
#[PublicPage] #[PublicPage]
#[RequireModeratorParticipant] #[RequireModeratorParticipant]
public function deleteRoom(): DataResponse { public function deleteRoom(): DataResponse {
if ($this->room->getType() === Room::TYPE_ONE_TO_ONE || $this->room->getType() === Room::TYPE_ONE_TO_ONE_FORMER) {
if (!$this->appConfig->getAppValueBool('delete_one_to_one_conversations')
&& in_array($this->room->getType(), [Room::TYPE_ONE_TO_ONE, Room::TYPE_ONE_TO_ONE_FORMER], true)) {
return new DataResponse(null, Http::STATUS_BAD_REQUEST); return new DataResponse(null, Http::STATUS_BAD_REQUEST);
} }
@ -1375,7 +1378,7 @@ class RoomController extends AEnvironmentAwareOCSController {
} }
if ($room->getType() !== Room::TYPE_CHANGELOG && if ($room->getType() !== Room::TYPE_CHANGELOG &&
$room->getObjectType() !== 'file' &&
$room->getObjectType() !== Room::OBJECT_TYPE_FILE &&
$this->participantService->getNumberOfUsers($room) === 1 && $this->participantService->getNumberOfUsers($room) === 1 &&
\in_array($participant->getAttendee()->getParticipantType(), [ \in_array($participant->getAttendee()->getParticipantType(), [
Participant::USER, Participant::USER,
@ -1386,6 +1389,12 @@ class RoomController extends AEnvironmentAwareOCSController {
return new DataResponse(null); return new DataResponse(null);
} }
if ($this->appConfig->getAppValueBool('delete_one_to_one_conversations')
&& in_array($this->room->getType(), [Room::TYPE_ONE_TO_ONE, Room::TYPE_ONE_TO_ONE_FORMER], true)) {
$this->roomService->deleteRoom($room);
return new DataResponse(null);
}
$currentUser = $this->userManager->get($this->userId); $currentUser = $this->userManager->get($this->userId);
if (!$currentUser instanceof IUser) { if (!$currentUser instanceof IUser) {
return new DataResponse(['error' => 'participant'], Http::STATUS_NOT_FOUND); return new DataResponse(['error' => 'participant'], Http::STATUS_NOT_FOUND);

9
lib/Service/RoomFormatter.php

@ -20,6 +20,7 @@ use OCA\Talk\Room;
use OCA\Talk\Webinary; use OCA\Talk\Webinary;
use OCP\App\IAppManager; use OCP\App\IAppManager;
use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Services\IAppConfig;
use OCP\AppFramework\Utility\ITimeFactory; use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Comments\IComment; use OCP\Comments\IComment;
use OCP\IConfig; use OCP\IConfig;
@ -36,6 +37,7 @@ use OCP\UserStatus\IUserStatus;
class RoomFormatter { class RoomFormatter {
public function __construct( public function __construct(
protected Config $talkConfig, protected Config $talkConfig,
protected IAppConfig $appConfig,
protected AvatarService $avatarService, protected AvatarService $avatarService,
protected ParticipantService $participantService, protected ParticipantService $participantService,
protected ChatManager $chatManager, protected ChatManager $chatManager,
@ -320,6 +322,13 @@ class RoomFormatter {
&& $room->getType() !== Room::TYPE_ONE_TO_ONE_FORMER && $room->getType() !== Room::TYPE_ONE_TO_ONE_FORMER
&& $currentParticipant->hasModeratorPermissions(false); && $currentParticipant->hasModeratorPermissions(false);
$roomData['canLeaveConversation'] = $room->getType() !== Room::TYPE_NOTE_TO_SELF; $roomData['canLeaveConversation'] = $room->getType() !== Room::TYPE_NOTE_TO_SELF;
if ($this->appConfig->getAppValueBool('delete_one_to_one_conversations')
&& in_array($room->getType(), [Room::TYPE_ONE_TO_ONE, Room::TYPE_ONE_TO_ONE_FORMER], true)) {
$roomData['canDeleteConversation'] = true;
$roomData['canLeaveConversation'] = false;
}
$roomData['canEnableSIP'] = $roomData['canEnableSIP'] =
$this->talkConfig->isSIPConfigured() $this->talkConfig->isSIPConfigured()
&& !preg_match(Room::SIP_INCOMPATIBLE_REGEX, $room->getToken()) && !preg_match(Room::SIP_INCOMPATIBLE_REGEX, $room->getToken())

10
tests/integration/features/chat-1/delete.feature

@ -235,3 +235,13 @@ Feature: chat/delete
Then user "participant2" sees the following system messages in room "room1" with 200 (v1) Then user "participant2" sees the following system messages in room "room1" with 200 (v1)
| room | actorType | actorId | actorDisplayName | systemMessage | | room | actorType | actorId | actorDisplayName | systemMessage |
| room1 | users | participant1 | participant1-displayname | history_cleared | | room1 | users | participant1 | participant1-displayname | history_cleared |
Scenario: Can delete chat history in one-to-one conversations when config is set
Given user "participant1" creates room "room" with 201 (v4)
| roomType | 1 |
| invite | participant2 |
And user "participant1" sends message "Message" to room "room" with 201
Then user "participant1" deletes chat history for room "room" with 403
When the following "spreed" app config is set
| delete_one_to_one_conversations | 1 |
Then user "participant1" deletes chat history for room "room" with 200

32
tests/integration/features/conversation-3/one-to-one.feature

@ -198,7 +198,7 @@ Feature: conversation-2/one-to-one
| users | participant1 | 1 | | users | participant1 | 1 |
| users | participant2 | 1 | | users | participant2 | 1 |
Scenario: Check share restrictions on one to one conversatio
Scenario: Check share restrictions on one to one conversation
Given the following "core" app config is set Given the following "core" app config is set
| shareapi_restrict_user_enumeration_full_match | no | | shareapi_restrict_user_enumeration_full_match | no |
| shareapi_allow_share_dialog_user_enumeration | yes | | shareapi_allow_share_dialog_user_enumeration | yes |
@ -207,3 +207,33 @@ Feature: conversation-2/one-to-one
And user "participant1" creates room "room15" with 403 (v4) And user "participant1" creates room "room15" with 403 (v4)
| roomType | 1 | | roomType | 1 |
| invite | participant2 | | invite | participant2 |
Scenario: Remove self from one-to-one conversations when deletable config is set deletes it
Given user "participant1" creates room "room" with 201 (v4)
| roomType | 1 |
| invite | participant2 |
Then user "participant1" removes themselves from room "room" with 200 (v4)
And user "participant1" is participant of the following rooms (v4)
And user "participant2" is participant of the following rooms (v4)
| id | type | participantType |
| room | 1 | 1 |
When user "participant1" creates room "room" with 200 (v4)
| roomType | 1 |
| invite | participant2 |
And the following "spreed" app config is set
| delete_one_to_one_conversations | 1 |
Then user "participant1" removes themselves from room "room" with 200 (v4)
And user "participant1" is participant of the following rooms (v4)
And user "participant2" is participant of the following rooms (v4)
Scenario: Deleting one-to-one conversations is possible when deletable config is set
Given user "participant1" creates room "room" with 201 (v4)
| roomType | 1 |
| invite | participant2 |
And user "participant1" sends message "Message" to room "room" with 201
Then user "participant1" deletes room "room" with 400 (v4)
When the following "spreed" app config is set
| delete_one_to_one_conversations | 1 |
Then user "participant1" deletes room "room" with 200 (v4)
And user "participant1" is participant of the following rooms (v4)
And user "participant2" is participant of the following rooms (v4)
Loading…
Cancel
Save