Browse Source

Introduce a Read-Only state for conversations

Signed-off-by: Joas Schilling <coding@schilljs.com>
pull/1640/head
Joas Schilling 7 years ago
parent
commit
f1667ac2f7
No known key found for this signature in database GPG Key ID: 7076EA9751AACDDA
  1. 2
      appinfo/info.xml
  2. 2
      appinfo/routes.php
  3. 6
      docs/api-v1.md
  4. 1
      lib/Capabilities.php
  5. 56
      lib/Controller/AEnvironmentAwareController.php
  6. 19
      lib/Controller/ChatController.php
  7. 21
      lib/Controller/RoomController.php
  8. 2
      lib/Manager.php
  9. 31
      lib/Middleware/Exceptions/ReadOnlyException.php
  10. 41
      lib/Middleware/InjectionMiddleware.php
  11. 58
      lib/Migration/Version5099Date20190319134820.php
  12. 11
      lib/Room.php
  13. 2
      tests/php/CapabilitiesTest.php

2
appinfo/info.xml

@ -17,7 +17,7 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m
]]></description>
<version>5.99.2</version>
<version>5.99.3</version>
<licence>agpl</licence>
<author>Daniel Calviño Sánchez</author>

2
appinfo/routes.php

@ -146,7 +146,7 @@ return [
'requirements' => ['apiVersion' => 'v1'],
],
[
'name' => 'Room#getRoom',
'name' => 'Room#getSingleRoom',
'url' => '/api/{apiVersion}/room/{token}',
'verb' => 'GET',
'requirements' => [

6
docs/api-v1.md

@ -45,6 +45,10 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`
* `2` group
* `3` public
### Read-only states
* `0` read-write
* `1` read-only
### Participant types
* `1` owner
* `2` moderator
@ -84,6 +88,7 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`
### 6.0
* `locked-one-to-one-rooms` - One-to-one conversations are now locked to the users. Neither guests nor other participants can be added, so the options to do that should be hidden as well. Also a user can only leave a one-to-one room (not delete). It will be deleted when the other participant left too. If the other participant posts a new chat message or starts a call, the left-participant will be re-added.
* `read-only-rooms` - Rooms can be in `read-only` mode which means people can not do calls or write chat messages.
## Room management
@ -137,6 +142,7 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`
`participantType` | int | Permissions level of the current user
`participantInCall` | bool | 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)
`count` | int | Number of active users
`numGuests` | int | Number of active guests
`lastPing` | int | Timestamp of the last ping of the current user (should be used for sorting)

1
lib/Capabilities.php

@ -66,6 +66,7 @@ class Capabilities implements IPublicCapability {
'notification-levels',
'invite-groups-and-mails',
'locked-one-to-one-rooms',
'read-only-rooms',
],
],
];

56
lib/Controller/AEnvironmentAwareController.php

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
* @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
*
* @author Lukas Reschke <lukas@statuscode.ch>
* @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\Spreed\Controller;
use OCA\Spreed\Participant;
use OCA\Spreed\Room;
use OCP\AppFramework\OCSController;
abstract class AEnvironmentAwareController extends OCSController {
/** @var Room */
protected $room;
/** @var Participant */
protected $participant;
public function setRoom(Room $room): void {
$this->room = $room;
}
public function getRoom(): ?Room {
return $this->room;
}
public function setParticipant(Participant $participant): void {
$this->participant = $participant;
}
public function getParticipant(): ?Participant {
return $this->participant;
}
}

19
lib/Controller/ChatController.php

@ -28,12 +28,10 @@ use OCA\Spreed\Chat\AutoComplete\Sorter;
use OCA\Spreed\Chat\ChatManager;
use OCA\Spreed\Chat\MessageParser;
use OCA\Spreed\GuestManager;
use OCA\Spreed\Participant;
use OCA\Spreed\Room;
use OCA\Spreed\TalkSession;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Collaboration\AutoComplete\IManager;
use OCP\Collaboration\Collaborators\ISearchResult;
@ -43,7 +41,7 @@ use OCP\IL10N;
use OCP\IRequest;
use OCP\IUserManager;
class ChatController extends OCSController {
class ChatController extends AEnvironmentAwareController {
/** @var string */
private $userId;
@ -80,11 +78,6 @@ class ChatController extends OCSController {
/** @var ITimeFactory */
protected $timeFactory;
/** @var Room */
private $room;
/** @var Participant */
private $participant;
public function __construct(string $appName,
?string $UserId,
IRequest $request,
@ -113,17 +106,10 @@ class ChatController extends OCSController {
$this->l = $l;
}
public function setRoom(Room $room): void {
$this->room = $room;
}
public function setParticipant(Participant $participant): void {
$this->participant = $participant;
}
/**
* @PublicPage
* @RequireParticipant
* @RequireReadWriteConversation
*
* Sends a new chat message to the given room.
*
@ -280,6 +266,7 @@ class ChatController extends OCSController {
/**
* @PublicPage
* @RequireParticipant
* @RequireReadWriteConversation
*
* @param string $search
* @param int $limit

21
lib/Controller/RoomController.php

@ -39,18 +39,16 @@ use OCA\Spreed\Room;
use OCA\Spreed\TalkSession;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Comments\IComment;
use OCP\IL10N;
use OCP\ILogger;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserManager;
use OCP\IGroup;
use OCP\IGroupManager;
class RoomController extends OCSController {
class RoomController extends AEnvironmentAwareController {
/** @var string|null */
private $userId;
/** @var TalkSession */
@ -72,11 +70,6 @@ class RoomController extends OCSController {
/** @var IL10N */
private $l10n;
/** @var Room */
private $room;
/** @var Participant */
private $participant;
public function __construct(string $appName,
?string $UserId,
IRequest $request,
@ -102,14 +95,6 @@ class RoomController extends OCSController {
$this->l10n = $l10n;
}
public function setRoom(Room $room): void {
$this->room = $room;
}
public function setParticipant(Participant $participant): void {
$this->participant = $participant;
}
/**
* Get all currently existent rooms which the user has joined
*
@ -138,7 +123,7 @@ class RoomController extends OCSController {
* @param string $token
* @return DataResponse
*/
public function getRoom(string $token): DataResponse {
public function getSingleRoom(string $token): DataResponse {
try {
$room = $this->manager->getRoomForParticipantByToken($token, $this->userId, true);
@ -177,6 +162,7 @@ class RoomController extends OCSController {
// Deprecated, use participantFlags instead.
'participantInCall' => false,
'participantFlags' => Participant::FLAG_DISCONNECTED,
'readOnly' => Room::READ_WRITE,
'count' => 0,
'hasPassword' => $room->hasPassword(),
'hasCall' => false,
@ -213,6 +199,7 @@ class RoomController extends OCSController {
// Deprecated, use participantFlags instead.
'participantInCall' => ($currentParticipant->getInCallFlags() & Participant::FLAG_IN_CALL) !== 0,
'participantFlags' => $currentParticipant->getInCallFlags(),
'readOnly' => $room->getReadOnly(),
'count' => $room->getNumberOfParticipants(false, $this->timeFactory->getTime() - 30),
'hasCall' => $room->getActiveSince() instanceof \DateTimeInterface,
'lastActivity' => $lastActivity,

2
lib/Manager.php

@ -119,7 +119,7 @@ class Manager {
]));
}
return new Room($this, $this->db, $this->secureRandom, $this->dispatcher, $this->timeFactory, $this->hasher, (int) $row['id'], (int) $row['type'], $row['token'], $row['name'], $row['password'], (int) $row['active_guests'], $activeSince, $lastActivity, $lastMessage, (string) $row['object_type'], (string) $row['object_id']);
return new Room($this, $this->db, $this->secureRandom, $this->dispatcher, $this->timeFactory, $this->hasher, (int) $row['id'], (int) $row['type'], (int) $row['read_only'], $row['token'], $row['name'], $row['password'], (int) $row['active_guests'], $activeSince, $lastActivity, $lastMessage, (string) $row['object_type'], (string) $row['object_id']);
}
/**

31
lib/Middleware/Exceptions/ReadOnlyException.php

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019 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\Spreed\Middleware\Exceptions;
use OCP\AppFramework\Http;
class ReadOnlyException extends \Exception {
public function __construct() {
parent::__construct('The conversation is read-only', Http::STATUS_FORBIDDEN);
}
}

41
lib/Middleware/InjectionMiddleware.php

@ -23,10 +23,14 @@ declare(strict_types=1);
namespace OCA\Spreed\Middleware;
use OC\AppFramework\Utility\ControllerMethodReflector;
use OCA\Spreed\Controller\AEnvironmentAwareController;
use OCA\Spreed\Controller\EnvironmentAwareTrait;
use OCA\Spreed\Exceptions\ParticipantNotFoundException;
use OCA\Spreed\Exceptions\RoomNotFoundException;
use OCA\Spreed\Manager;
use OCA\Spreed\Middleware\Exceptions\NotAModeratorException;
use OCA\Spreed\Middleware\Exceptions\ReadOnlyException;
use OCA\Spreed\Room;
use OCA\Spreed\TalkSession;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
@ -69,8 +73,13 @@ class InjectionMiddleware extends Middleware {
* @throws RoomNotFoundException
* @throws ParticipantNotFoundException
* @throws NotAModeratorException
* @throws ReadOnlyException
*/
public function beforeController($controller, $methodName): void {
if (!$controller instanceof AEnvironmentAwareController) {
return;
}
if ($this->reflector->hasAnnotation('RequireLoggedInParticipant')) {
$this->getLoggedIn($controller, false);
}
@ -86,9 +95,18 @@ class InjectionMiddleware extends Middleware {
if ($this->reflector->hasAnnotation('RequireModeratorParticipant')) {
$this->getLoggedInOrGuest($controller, true);
}
if ($this->reflector->hasAnnotation('RequireReadWriteConversation')) {
$this->checkReadOnlyState($controller);
}
}
protected function getLoggedIn(Controller $controller, bool $moderatorRequired): void {
/**
* @param AEnvironmentAwareController $controller
* @param bool $moderatorRequired
* @throws NotAModeratorException
*/
protected function getLoggedIn(AEnvironmentAwareController $controller, bool $moderatorRequired): void {
$token = $this->request->getParam('token');
$room = $this->manager->getRoomForParticipantByToken($token, $this->userId);
$participant = $room->getParticipant($this->userId);
@ -101,7 +119,12 @@ class InjectionMiddleware extends Middleware {
$controller->setParticipant($participant);
}
protected function getLoggedInOrGuest(Controller $controller, bool $moderatorRequired): void {
/**
* @param AEnvironmentAwareController $controller
* @param bool $moderatorRequired
* @throws NotAModeratorException
*/
protected function getLoggedInOrGuest(AEnvironmentAwareController $controller, bool $moderatorRequired): void {
$token = $this->request->getParam('token');
$room = $this->manager->getRoomForParticipantByToken($token, $this->userId);
if ($this->userId !== null) {
@ -119,6 +142,17 @@ class InjectionMiddleware extends Middleware {
$controller->setParticipant($participant);
}
/**
* @param AEnvironmentAwareController $controller
* @throws ReadOnlyException
*/
protected function checkReadOnlyState(AEnvironmentAwareController $controller): void {
$room = $controller->getRoom();
if (!$room instanceof Room || $room->getReadOnly() === Room::READ_ONLY) {
throw new ReadOnlyException();
}
}
/**
* @param Controller $controller
* @param string $methodName
@ -136,7 +170,8 @@ class InjectionMiddleware extends Middleware {
return new RedirectToDefaultAppResponse();
}
if ($exception instanceof NotAModeratorException) {
if ($exception instanceof NotAModeratorException ||
$exception instanceof ReadOnlyException) {
if ($controller instanceof OCSController) {
throw new OCSException('', Http::STATUS_FORBIDDEN);
}

58
lib/Migration/Version5099Date20190319134820.php

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, 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\Spreed\Migration;
use Closure;
use Doctrine\DBAL\Types\Type;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\SimpleMigrationStep;
use OCP\Migration\IOutput;
class Version5099Date20190319134820 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) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
if ($schema->hasTable('talk_rooms')) {
$table = $schema->getTable('talk_rooms');
if (!$table->hasColumn('read_only')) {
$table->addColumn('read_only', Type::INTEGER, [
'notnull' => true,
'length' => 6,
'default' => 0,
]);
}
}
return $schema;
}
}

11
lib/Room.php

@ -45,6 +45,9 @@ class Room {
public const GROUP_CALL = 2;
public const PUBLIC_CALL = 3;
public const READ_WRITE = 0;
public const READ_ONLY = 1;
public const PARTICIPANT_REMOVED = 'remove';
public const PARTICIPANT_LEFT = 'leave';
@ -65,6 +68,8 @@ class Room {
private $id;
/** @var int */
private $type;
/** @var int */
private $readOnly;
/** @var string */
private $token;
/** @var string */
@ -97,6 +102,7 @@ class Room {
IHasher $hasher,
int $id,
int $type,
int $readOnly,
string $token,
string $name,
string $password,
@ -114,6 +120,7 @@ class Room {
$this->hasher = $hasher;
$this->id = $id;
$this->type = $type;
$this->readOnly = $readOnly;
$this->token = $token;
$this->name = $name;
$this->password = $password;
@ -133,6 +140,10 @@ class Room {
return $this->type;
}
public function getReadOnly(): int {
return $this->readOnly;
}
public function getToken(): string {
return $this->token;
}

2
tests/php/CapabilitiesTest.php

@ -78,6 +78,7 @@ class CapabilitiesTest extends TestCase {
'notification-levels',
'invite-groups-and-mails',
'locked-one-to-one-rooms',
'read-only-rooms',
],
],
], $capabilities->getCapabilities());
@ -119,6 +120,7 @@ class CapabilitiesTest extends TestCase {
'notification-levels',
'invite-groups-and-mails',
'locked-one-to-one-rooms',
'read-only-rooms',
],
],
], $capabilities->getCapabilities());

Loading…
Cancel
Save