Skip to content

Commit

Permalink
Add listable flags attribute for conversations
Browse files Browse the repository at this point in the history
Added ability to set a conversation as listable for regular users and/or
guest users from the guest app.

This only implements the flag, endpoint and UI to manage it but not yet
making it appear in search results.

Signed-off-by: Vincent Petry <vincent@nextcloud.com>
  • Loading branch information
PVince81 committed Dec 10, 2020
1 parent 2d68af4 commit 139264a
Show file tree
Hide file tree
Showing 26 changed files with 426 additions and 3 deletions.
9 changes: 9 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,15 @@
'token' => '^[a-z0-9]{4,30}$',
],
],
[
'name' => 'Room#setListable',
'url' => '/api/{apiVersion}/room/{token}/listable',
'verb' => 'PUT',
'requirements' => [
'apiVersion' => 'v3',
'token' => '^[a-z0-9]{4,30}$',
],
],
[
'name' => 'Room#setPassword',
'url' => '/api/{apiVersion}/room/{token}/password',
Expand Down
1 change: 1 addition & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@ title: Capabilities
* `config => previews => max-gif-size` - Maximum size in bytes below which a GIF can be embedded directly in the page at render time. Bigger files will be rendered statically using the preview endpoint instead. Can be set with `occ config:app:set spreed max-gif-size --value=X` where X is the new value in bytes. Defaults to 3 MB.
* `chat-read-status` - On conversation API v3 and the chat API the last common read message is exposed which can be used to update the "read status" flag of own chat messages. The info should be shown only when the user also shares their read status. The user's value can be found in `config => chat => read-privacy`.
* `config => chat => read-privacy` - See `chat-read-status`
* `listable-rooms` - Conversations can searched for even when not joined. A "listable" attribute set on rooms defines the scope of who can find it.
5 changes: 4 additions & 1 deletion docs/chat.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`
`status` | string | Optional: Only available with `includeStatus=true` and for users with a set status
`statusIcon` | string | Optional: Only available with `includeStatus=true` and for users with a set status
`statusMessage` | string | Optional: Only available with `includeStatus=true` and for users with a set status

## System messages

* `conversation_created` - {actor} created the conversation
Expand All @@ -160,6 +160,9 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`
* `call_ended` - Call with {user1}, {user2}, {user3}, {user4} and {user5} (Duration 30:23)
* `read_only_off` - {actor} unlocked the conversation
* `read_only` - {actor} locked the conversation
* `listable_participants` - {actor} made the conversation listable only for participants
* `listable_users` - {actor} made the conversation listable for regular users
* `listable_all` - {actor} made the conversation listable for everone which includes users and guest users
* `lobby_timer_reached` - The conversation is now open to everyone
* `lobby_none` - {actor} opened the conversation to everyone
* `lobby_non_moderators` - {actor} restricted the conversation to moderators
Expand Down
5 changes: 5 additions & 0 deletions docs/constants.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ title: Constants
* `0` read-write
* `1` read-only

## Listable scope
* `0` participants only
* `1` regular users only, excluding guests
* `2` everyone

## Participant types
* `1` owner
* `2` moderator
Expand Down
18 changes: 18 additions & 0 deletions docs/conversation.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
`participantInCall` | bool | 🏴 v1 | Flag if the current user is in the call (deprecated, use `participantFlags` instead)
`participantFlags` | int | * | Flags of the current user (only available with `in-call-flags` capability)
`readOnly` | int | * | Read-only state for the current user (only available with `read-only-rooms` capability)
`listable` | int | * | Listable scope for the room (only available with `listable-rooms` capability)
`count` | int | 🏴 v1 | **Deprecated:** ~~Number of active users~~ - always returns `0`
`numGuests` | int | 🏴 v1 | Number of active guests
`lastPing` | int | * | Timestamp of the last ping of the current user (should be used for sorting)
Expand Down Expand Up @@ -266,3 +267,20 @@
+ `400 Bad Request` When the the given level is invalid
+ `401 Unauthorized` When the participant is a guest
+ `404 Not Found` When the conversation could not be found for the participant

## Set listable scope for a conversation

* Method: `PUT`
* Endpoint: `/room/{token}/listable`
* Data:

field | type | Description
------|------|------------
`scope` | int | New flags for the conversation

* Response:
- Status code:
+ `200 OK`
+ `400 Bad Request` When the conversation type does not support making it listable (only group and public conversation)
+ `403 Forbidden` When the current user is not a moderator/owner or the conversation is not a public conversation
+ `404 Not Found` When the conversation could not be found for the participant
7 changes: 7 additions & 0 deletions docs/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ Explanations:
* After event name: `OCA\Talk\Room::EVENT_AFTER_READONLY_SET`
* Since: 8.0.0

### Set listable

* Event class: `OCA\Talk\Events\ModifyRoomEvent`
* Before event name: `OCA\Talk\Room::EVENT_BEFORE_LISTABLE_SET`
* After event name: `OCA\Talk\Room::EVENT_AFTER_LISTABLE_SET`
* Since: 11.0.0

### Set lobby

* Event class: `OCA\Talk\Events\ModifyLobbyEvent`
Expand Down
1 change: 1 addition & 0 deletions lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public function getCapabilities(): array {
'invite-groups-and-mails',
'locked-one-to-one-rooms',
'read-only-rooms',
'listable-rooms',
'chat-read-marker',
'webinary-lobby',
'start-call-flag',
Expand Down
21 changes: 21 additions & 0 deletions lib/Chat/Parser/SystemMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,27 @@ public function parseMessage(Message $chatMessage): void {
} elseif ($cliIsActor) {
$parsedMessage = $this->l->t('An administrator locked the conversation');
}
} elseif ($message === 'listable_participants') {
$parsedMessage = $this->l->t('{actor} made the conversation listable for participants only');
if ($currentUserIsActor) {
$parsedMessage = $this->l->t('You made the conversation listable for participants only');
} elseif ($cliIsActor) {
$parsedMessage = $this->l->t('An administrator made the conversation listable for participants only');
}
} elseif ($message === 'listable_users') {
$parsedMessage = $this->l->t('{actor} made the conversation listable for users only');
if ($currentUserIsActor) {
$parsedMessage = $this->l->t('You made the conversation listable for users only');
} elseif ($cliIsActor) {
$parsedMessage = $this->l->t('An administrator made the conversation listable for users only');
}
} elseif ($message === 'listable_all') {
$parsedMessage = $this->l->t('{actor} made the conversation listable for everyone');
if ($currentUserIsActor) {
$parsedMessage = $this->l->t('You made the conversation listable for everyone');
} elseif ($cliIsActor) {
$parsedMessage = $this->l->t('An administrator made the conversation listable for everyone');
}
} elseif ($message === 'lobby_timer_reached') {
$parsedMessage = $this->l->t('The conversation is now open to everyone');
} elseif ($message === 'lobby_none') {
Expand Down
18 changes: 18 additions & 0 deletions lib/Chat/SystemMessage/Listener.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,24 @@ public static function register(IEventDispatcher $dispatcher): void {
$listener->sendSystemMessage($room, 'read_only_off');
}
});
$dispatcher->addListener(Room::EVENT_AFTER_LISTABLE_SET, static function (ModifyRoomEvent $event) {
$room = $event->getRoom();

if ($room->getType() === Room::CHANGELOG_CONVERSATION) {
return;
}

/** @var self $listener */
$listener = \OC::$server->query(self::class);

if ($event->getNewValue() === Room::LISTABLE_PARTICIPANTS) {
$listener->sendSystemMessage($room, 'listable_participants');
} elseif ($event->getNewValue() === Room::LISTABLE_USERS) {
$listener->sendSystemMessage($room, 'listable_users');
} elseif ($event->getNewValue() === Room::LISTABLE_ALL) {
$listener->sendSystemMessage($room, 'listable_all');
}
});
$dispatcher->addListener(Room::EVENT_AFTER_LOBBY_STATE_SET, static function (ModifyLobbyEvent $event) {
if ($event->getNewValue() === $event->getOldValue()) {
return;
Expand Down
17 changes: 17 additions & 0 deletions lib/Command/Room/Create.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ protected function configure(): void {
null,
InputOption::VALUE_NONE,
'Creates the room with read-only access only if set'
)->addOption(
'listable',
null,
InputOption::VALUE_NONE,
'Creates the room with the given listable scope'
)->addOption(
'password',
null,
Expand All @@ -95,10 +100,21 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$groups = $input->getOption('group');
$public = $input->getOption('public');
$readonly = $input->getOption('readonly');
$listable = $input->getOption('listable');
$password = $input->getOption('password');
$owner = $input->getOption('owner');
$moderators = $input->getOption('moderator');

if (!in_array($readOnly, [null, '0', '1'], true)) {
$output->writeln('<error>Invalid value for option "--readonly" given.</error>');
return 1;
}

if (!in_array($listable, [null, '0', '1', '2', '3'], true)) {
$output->writeln('<error>Invalid value for option "--listable" given.</error>');
return 1;
}

$roomType = $public ? Room::PUBLIC_CALL : Room::GROUP_CALL;
try {
$room = $this->roomService->createConversation($roomType, $name);
Expand All @@ -116,6 +132,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}

$this->setRoomReadOnly($room, $readonly);
$this->setListable($room, $listable);

if ($password !== null) {
$this->setRoomPassword($room, $password);
Expand Down
16 changes: 16 additions & 0 deletions lib/Command/Room/TRoomCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,22 @@ protected function setRoomReadOnly(Room $room, bool $readOnly): void {
}
}

/**
* @param Room $room
* @param int $listable
*
* @throws InvalidArgumentException
*/
protected function setRoomListable(Room $room, int $listable): void {
if ($room->getListable() === $listable) {
return;
}

if (!$room->setListable($listable)) {
throw new InvalidArgumentException('Unable to change room state.');
}
}

/**
* @param Room $room
* @param string $password
Expand Down
17 changes: 17 additions & 0 deletions lib/Command/Room/Update.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ protected function configure(): void {
null,
InputOption::VALUE_REQUIRED,
'Modifies the room to be read-only (value 1) or read-write (value 0)'
)->addOption(
'listable',
null,
InputOption::VALUE_NONE,
'Modifies the room\'s listable scope'
)->addOption(
'password',
null,
Expand All @@ -85,6 +90,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$description = $input->getOption('description');
$public = $input->getOption('public');
$readOnly = $input->getOption('readonly');
$listable = $input->getOption('listable');
$password = $input->getOption('password');
$owner = $input->getOption('owner');

Expand All @@ -98,6 +104,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return 1;
}

if (!in_array($listable, [null, '0', '1', '2', '3'], true)) {
$output->writeln('<error>Invalid value for option "--listable" given.</error>');
return 1;
}

try {
$room = $this->manager->getRoomByToken($token);
} catch (RoomNotFoundException $e) {
Expand Down Expand Up @@ -127,6 +138,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$this->setRoomReadOnly($room, ($readOnly === '1'));
}

if ($listable !== null) {
$this->setRoomListable($room, (int)$listable);
}

if ($password !== null) {
$this->setRoomPassword($room, $password);
}
Expand All @@ -152,6 +167,8 @@ public function completeOptionValues($optionName, CompletionContext $context) {
case 'public':
case 'readonly':
return ['1', '0'];
case 'listable':
return ['2', '1', '0'];

case 'owner':
return $this->completeParticipantValues($context);
Expand Down
17 changes: 17 additions & 0 deletions lib/Controller/RoomController.php
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@ protected function formatRoomV2andV3(Room $room, ?Participant $currentParticipan
'participantType' => Participant::GUEST,
'participantFlags' => Participant::FLAG_DISCONNECTED,
'readOnly' => Room::READ_WRITE,
'listable' => $room->getListable(),
'hasPassword' => $room->hasPassword(),
'hasCall' => false,
'canStartCall' => false,
Expand Down Expand Up @@ -605,6 +606,7 @@ protected function formatRoomV2andV3(Room $room, ?Participant $currentParticipan
'objectId' => $room->getObjectId(),
'participantType' => $attendee->getParticipantType(),
'readOnly' => $room->getReadOnly(),
'listable' => $room->getListable(),
'hasCall' => $room->getActiveSince() instanceof \DateTimeInterface,
'lastActivity' => $lastActivity,
'isFavorite' => $attendee->isFavorite(),
Expand Down Expand Up @@ -1469,6 +1471,21 @@ public function setReadOnly(int $state): DataResponse {
return new DataResponse();
}

/**
* @NoAdminRequired
* @RequireModeratorParticipant
*
* @param int $state
* @return DataResponse
*/
public function setListable(int $scope): DataResponse {
if (!$this->room->setListable($scope)) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}

return new DataResponse();
}

/**
* @PublicPage
* @RequireModeratorParticipant
Expand Down
2 changes: 2 additions & 0 deletions lib/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ public function createRoomObject(array $row): Room {
(int) $row['r_id'],
(int) $row['type'],
(int) $row['read_only'],
(int) $row['listable'],
(int) $row['lobby_state'],
(int) $row['sip_enabled'],
$assignedSignalingServer,
Expand Down Expand Up @@ -704,6 +705,7 @@ public function getChangelogRoom(string $userId): Room {
if ($row === false) {
$room = $this->createRoom(Room::CHANGELOG_CONVERSATION, $userId);
$room->setReadOnly(Room::READ_ONLY);
$room->setListable(Room::LISTABLE_PARTICIPANTS);

$this->participantService->addUsers($room,[[
'actorType' => Attendee::ACTOR_USERS,
Expand Down
60 changes: 60 additions & 0 deletions lib/Migration/Version2100Date20201201102528.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2020, Vincent Petry <vincent@nextcloud.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\Migration;

use Closure;
use Doctrine\DBAL\Types\Type;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

/**
* Add listable column to the rooms table.
*/
class Version2100Date20201201102528 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();

if ($schema->hasTable('talk_rooms')) {
$table = $schema->getTable('talk_rooms');

if (!$table->hasColumn('listable')) {
$table->addColumn('listable', Type::INTEGER, [
'notnull' => true,
'length' => 6,
'default' => 0,
]);
}
}

return $schema;
}
}
Loading

0 comments on commit 139264a

Please sign in to comment.