Skip to content

Commit

Permalink
Merge pull request #1640 from nextcloud/feature/383/read-only-convers…
Browse files Browse the repository at this point in the history
…ations

Read only conversations
  • Loading branch information
nickvergessen authored Mar 26, 2019
2 parents 7aa3ecd + 396bcc7 commit ca6e9fe
Show file tree
Hide file tree
Showing 33 changed files with 671 additions and 170 deletions.
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
11 changes: 10 additions & 1 deletion appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@
'requirements' => ['apiVersion' => 'v1'],
],
[
'name' => 'Room#getRoom',
'name' => 'Room#getSingleRoom',
'url' => '/api/{apiVersion}/room/{token}',
'verb' => 'GET',
'requirements' => [
Expand Down Expand Up @@ -190,6 +190,15 @@
'token' => '^[a-z0-9]{4,30}$',
],
],
[
'name' => 'Room#setReadOnly',
'url' => '/api/{apiVersion}/room/{token}/read-only',
'verb' => 'PUT',
'requirements' => [
'apiVersion' => 'v1',
'token' => '^[a-z0-9]{4,30}$',
],
],
[
'name' => 'Room#setPassword',
'url' => '/api/{apiVersion}/room/{token}/password',
Expand Down
8 changes: 4 additions & 4 deletions css/chatview.scss
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@
border: none;
opacity: .3;
}
#chatView .newCommentForm .submit:hover,
#chatView .newCommentForm .submit:focus,
#chatView .newCommentForm .share:hover,
#chatView .newCommentForm .share:focus {
#chatView .newCommentForm .submit:hover:not(:disabled),
#chatView .newCommentForm .submit:focus:not(:disabled),
#chatView .newCommentForm .share:hover:not(:disabled),
#chatView .newCommentForm .share:focus:not(:disabled) {
opacity: 1;
}

Expand Down
29 changes: 29 additions & 0 deletions docs/api-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
* [Get user´s rooms](#get-user-s-rooms)
* [Get single room (also for guests)](#get-single-room-also-for-guests)
* [Rename a room](#rename-a-room)
* [Set read-only for a room](#set-read-only-for-a-room)
* [Set password for a room](#set-password-for-a-room)
* [Delete a room](#delete-a-room)
* [Allow guests in a room (public room)](#allow-guests-in-a-room-public-room)
* [Disallow guests in a room (group room)](#disallow-guests-in-a-room-group-room)
Expand All @@ -29,6 +31,8 @@
- [Chat](#chat)
* [Receive chat messages of a room](#receive-chat-messages-of-a-room)
* [Sending a new chat message](#sending-a-new-chat-message)
* [Get mention autocomplete suggestions](#get-mention-autocomplete-suggestions)
* [System messages](#system-messages)
- [Guests](#guests)
* [Set display name](#set-display-name)
- [Signaling](#signaling)
Expand All @@ -45,6 +49,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
Expand Down Expand Up @@ -84,6 +92,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

Expand Down Expand Up @@ -137,6 +146,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)
Expand Down Expand Up @@ -182,6 +192,23 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`
+ `404 Not Found` When the room could not be found for the participant
+ `405 Method Not Allowed` When the room is a one to one room

### Set read-only for a room

* Method: `PUT`
* Endpoint: `/room/{token}/read-only`
* Data:

field | type | Description
------|------|------------
`state` | int | New state for the room

* Response:
- Header:
+ `200 OK`
+ `400 Bad Request` When the room type does not support read-only (only group and public room atm)
+ `403 Forbidden` When the current user is not a moderator/owner or the room is not a public room
+ `404 Not Found` When the room could not be found for the participant

### Set password for a room

* Method: `PUT`
Expand Down Expand Up @@ -597,6 +624,8 @@ Base endpoint is: `/ocs/v2.php/apps/spreed/api/v1`
* `moderator_demoted` - {actor} demoted {user} from moderator
* `guest_moderator_promoted` - {actor} promoted {user} to moderator
* `guest_moderator_demoted` - {actor} demoted {user} from moderator
* `read_only_off` - {actor} unlocked the conversation
* `read_only` - {actor} locked the conversation

## Guests

Expand Down
2 changes: 2 additions & 0 deletions js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@
});
this._sidebarView.setCallInfoView(callInfoView);

this._chatView.setRoom(this.activeRoom);
this._messageCollection.setRoomToken(this.activeRoom.get('token'));
this._messageCollection.receiveMessages();
},
Expand Down Expand Up @@ -437,6 +438,7 @@
this._messageCollection = new OCA.SpreedMe.Models.ChatMessageCollection(null, {token: null});
this._chatView = new OCA.SpreedMe.Views.ChatView({
collection: this._messageCollection,
model: this.activeRoom,
id: 'chatView',
guestNameModel: this._localStorageModel
});
Expand Down
2 changes: 2 additions & 0 deletions js/embedded.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
self._rooms.forEach(function(room) {
if (room.get('token') === token) {
self.activeRoom = room;
self._chatView.setRoom(room);
}
});
}
Expand All @@ -106,6 +107,7 @@
this._messageCollection = new OCA.SpreedMe.Models.ChatMessageCollection(null, {token: null});
this._chatView = new OCA.SpreedMe.Views.ChatView({
collection: this._messageCollection,
model: this.activeRoom,
id: 'chatView',
guestNameModel: this._localStorageModel
});
Expand Down
5 changes: 5 additions & 0 deletions js/views/callbutton.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@

templateContext: function() {
return {
isReadOnly: this.model.get('readOnly') === 1,
isInCall: (this.model.get('participantFlags') & OCA.SpreedMe.app.FLAG_IN_CALL) !== 0,
hasCall: this.model.get('hasCall'),
leaveCallText: t('spreed', 'Leave call'),
joinCallText: t('spreed', 'Join call'),
startCallText: t('spreed', 'Start call'),
readOnlyText: t('spreed', 'Calls are disabled in this conversation.'),
};
},

Expand All @@ -71,6 +73,9 @@
'change:participantFlags': function() {
this.render();
},
'change:readOnly': function() {
this.render();
},
},

/**
Expand Down
26 changes: 23 additions & 3 deletions js/views/chatview.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@
'paste div.message': '_onPaste'
},

modelEvents: {
'change:readOnly': function() {
this.render();
}
},

initialize: function() {
this.listenTo(this.collection, 'reset', this.render);
this.listenTo(this.collection, 'add:start', this._onAddModelStart);
Expand All @@ -73,6 +79,10 @@
_.bindAll(this, '_onAutoComplete');
},

setRoom: function(model) {
this.model = model;
},

_initAutoComplete: function($target) {
var s = this;
var limit = 20;
Expand Down Expand Up @@ -182,12 +192,22 @@
this._addCommentTemplate = OCA.Talk.Views.Templates['chatview_add_comment'];
}

var isReadOnly = this.model && this.model.get('readOnly') === 1;
var newMessagePlaceholder = t('spreed', 'New message …');
var submitText = t('spreed', 'Send');
if (isReadOnly) {
newMessagePlaceholder = t('spreed', 'You can not send messages, because the conversation is locked.');
submitText = t('spreed', 'The conversation is locked.');
}

return this._addCommentTemplate(_.extend({
actorId: OC.getCurrentUser().uid,
actorDisplayName: OC.getCurrentUser().displayName,
newMessagePlaceholder: t('spreed', 'New message …'),
submitText: t('spreed', 'Send'),
shareText: t('spreed', 'Share')
newMessagePlaceholder: newMessagePlaceholder,
submitText: submitText,
shareText: t('spreed', 'Share'),
isReadOnly: isReadOnly,
canShare: !isReadOnly && OC.getCurrentUser().uid,
}, params));
},

Expand Down
38 changes: 30 additions & 8 deletions js/views/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,35 @@
templates['callbutton'] = template({"1":function(container,depth0,helpers,partials,data) {
var helper;

return "<button class=\"leave-call primary\">"
return " <button class=\"leave-call primary\">"
+ container.escapeExpression(((helper = (helper = helpers.leaveCallText || (depth0 != null ? depth0.leaveCallText : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"leaveCallText","hash":{},"data":data}) : helper)))
+ "<span class=\"icon icon-loading-small hidden\"></span></button>\n";
},"3":function(container,depth0,helpers,partials,data) {
var stack1;

return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.hasCall : depth0),{"name":"if","hash":{},"fn":container.program(4, data, 0),"inverse":container.program(6, data, 0),"data":data})) != null ? stack1 : "");
return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.isReadOnly : depth0),{"name":"if","hash":{},"fn":container.program(4, data, 0),"inverse":container.program(6, data, 0),"data":data})) != null ? stack1 : "");
},"4":function(container,depth0,helpers,partials,data) {
var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;

return " <button class=\"join-call primary has-tooltip\" title=\""
+ alias4(((helper = (helper = helpers.readOnlyText || (depth0 != null ? depth0.readOnlyText : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"readOnlyText","hash":{},"data":data}) : helper)))
+ "\" disabled=\"\">"
+ alias4(((helper = (helper = helpers.startCallText || (depth0 != null ? depth0.startCallText : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"startCallText","hash":{},"data":data}) : helper)))
+ "</button>\n";
},"6":function(container,depth0,helpers,partials,data) {
var stack1;

return ((stack1 = helpers["if"].call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.hasCall : depth0),{"name":"if","hash":{},"fn":container.program(7, data, 0),"inverse":container.program(9, data, 0),"data":data})) != null ? stack1 : "");
},"7":function(container,depth0,helpers,partials,data) {
var helper;

return "<button class=\"join-call call-ongoing primary\">"
return " <button class=\"join-call call-ongoing primary\">"
+ container.escapeExpression(((helper = (helper = helpers.joinCallText || (depth0 != null ? depth0.joinCallText : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"joinCallText","hash":{},"data":data}) : helper)))
+ "<span class=\"icon icon-loading-small hidden\"></span></button>\n";
},"6":function(container,depth0,helpers,partials,data) {
},"9":function(container,depth0,helpers,partials,data) {
var helper;

return "<button class=\"join-call primary\">"
return " <button class=\"join-call primary\">"
+ container.escapeExpression(((helper = (helper = helpers.startCallText || (depth0 != null ? depth0.startCallText : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"startCallText","hash":{},"data":data}) : helper)))
+ "<span class=\"icon icon-loading-small hidden\"></span></button>\n";
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
Expand All @@ -43,6 +55,12 @@ templates['chatview_add_comment'] = template({"1":function(container,depth0,help
},"3":function(container,depth0,helpers,partials,data) {
return " <div class=\"guest-name\"></div>\n";
},"5":function(container,depth0,helpers,partials,data) {
return "false";
},"7":function(container,depth0,helpers,partials,data) {
return "true";
},"9":function(container,depth0,helpers,partials,data) {
return "disabled=\"\"";
},"11":function(container,depth0,helpers,partials,data) {
var helper;

return " <button class=\"share icon-add has-tooltip\" title=\""
Expand All @@ -55,14 +73,18 @@ templates['chatview_add_comment'] = template({"1":function(container,depth0,help
+ alias4(((helper = (helper = helpers.actorId || (depth0 != null ? depth0.actorId : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"actorId","hash":{},"data":data}) : helper)))
+ "\"></div>\n"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.actorId : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.program(3, data, 0),"data":data})) != null ? stack1 : "")
+ " </div>\n <form class=\"newCommentForm\">\n <div contentEditable=\"true\" class=\"message\" data-placeholder=\""
+ " </div>\n <form class=\"newCommentForm\">\n <div contentEditable=\""
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isReadOnly : depth0),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.program(7, data, 0),"data":data})) != null ? stack1 : "")
+ "\" class=\"message\" data-placeholder=\""
+ alias4(((helper = (helper = helpers.newMessagePlaceholder || (depth0 != null ? depth0.newMessagePlaceholder : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"newMessagePlaceholder","hash":{},"data":data}) : helper)))
+ "\">"
+ alias4(((helper = (helper = helpers.message || (depth0 != null ? depth0.message : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"message","hash":{},"data":data}) : helper)))
+ "</div>\n <input class=\"submit icon-confirm has-tooltip\" type=\"submit\" value=\"\" title=\""
+ "</div>\n <input class=\"submit icon-confirm has-tooltip\" type=\"submit\" "
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isReadOnly : depth0),{"name":"if","hash":{},"fn":container.program(9, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " value=\"\" title=\""
+ alias4(((helper = (helper = helpers.submitText || (depth0 != null ? depth0.submitText : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"submitText","hash":{},"data":data}) : helper)))
+ "\"/>\n <div class=\"submitLoading icon-loading-small hidden\"></div>\n"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.actorId : depth0),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.canShare : depth0),{"name":"if","hash":{},"fn":container.program(11, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " </form>\n</div>\n";
},"useData":true});
templates['chatview_comment'] = template({"1":function(container,depth0,helpers,partials,data) {
Expand Down
12 changes: 8 additions & 4 deletions js/views/templates/callbutton.handlebars
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
{{#if isInCall}}
<button class="leave-call primary">{{leaveCallText}}<span class="icon icon-loading-small hidden"></span></button>
<button class="leave-call primary">{{leaveCallText}}<span class="icon icon-loading-small hidden"></span></button>
{{else}}
{{#if hasCall}}
<button class="join-call call-ongoing primary">{{joinCallText}}<span class="icon icon-loading-small hidden"></span></button>
{{#if isReadOnly}}
<button class="join-call primary has-tooltip" title="{{readOnlyText}}" disabled="">{{startCallText}}</button>
{{else}}
<button class="join-call primary">{{startCallText}}<span class="icon icon-loading-small hidden"></span></button>
{{#if hasCall}}
<button class="join-call call-ongoing primary">{{joinCallText}}<span class="icon icon-loading-small hidden"></span></button>
{{else}}
<button class="join-call primary">{{startCallText}}<span class="icon icon-loading-small hidden"></span></button>
{{/if}}
{{/if}}
{{/if}}
6 changes: 3 additions & 3 deletions js/views/templates/chatview_add_comment.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
{{/if}}
</div>
<form class="newCommentForm">
<div contentEditable="true" class="message" data-placeholder="{{newMessagePlaceholder}}">{{message}}</div>
<input class="submit icon-confirm has-tooltip" type="submit" value="" title="{{submitText}}"/>
<div contentEditable="{{#if isReadOnly}}false{{else}}true{{/if}}" class="message" data-placeholder="{{newMessagePlaceholder}}">{{message}}</div>
<input class="submit icon-confirm has-tooltip" type="submit" {{#if isReadOnly}}disabled=""{{/if}} value="" title="{{submitText}}"/>
<div class="submitLoading icon-loading-small hidden"></div>
{{#if actorId}}
{{#if canShare}}
<button class="share icon-add has-tooltip" title="{{shareText}}"></button>
<div class="shareLoading icon-loading-small hidden"></div>
{{/if}}
Expand Down
1 change: 1 addition & 0 deletions lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public function getCapabilities(): array {
'notification-levels',
'invite-groups-and-mails',
'locked-one-to-one-rooms',
'read-only-rooms',
],
],
];
Expand Down
11 changes: 11 additions & 0 deletions lib/Chat/Parser/SystemMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use OCA\Spreed\GuestManager;
use OCA\Spreed\Model\Message;
use OCA\Spreed\Participant;
use OCA\Spreed\Room;
use OCA\Spreed\Share\RoomShareProvider;
use OCP\Comments\IComment;
use OCP\Files\IRootFolder;
Expand Down Expand Up @@ -126,6 +127,16 @@ public function parseMessage(Message $chatMessage): void {
}
} else if ($message === 'call_ended') {
[$parsedMessage, $parsedParameters] = $this->parseCall($parameters);
} else if ($message === 'read_only_off') {
$parsedMessage = $this->l->t('{actor} unlocked the conversation');
if ($currentUserIsActor) {
$parsedMessage = $this->l->t('You unlocked the conversation');
}
} else if ($message === 'read_only') {
$parsedMessage = $this->l->t('{actor} locked the conversation');
if ($currentUserIsActor) {
$parsedMessage = $this->l->t('You locked the conversation');
}
} else if ($message === 'guests_allowed') {
$parsedMessage = $this->l->t('{actor} allowed guests');
if ($currentUserIsActor) {
Expand Down
15 changes: 15 additions & 0 deletions lib/Chat/SystemMessage/Listener.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,21 @@ public static function register(EventDispatcherInterface $dispatcher): void {
$listener->sendSystemMessage($room, 'guests_disallowed', $event->getArguments());
}
});
$dispatcher->addListener(Room::class . '::postSetReadOnly', function(GenericEvent $event) {
$arguments = $event->getArguments();

/** @var Room $room */
$room = $event->getSubject();

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

if ($arguments['newState'] === Room::READ_ONLY) {
$listener->sendSystemMessage($room, 'read_only', $event->getArguments());
} else if ($arguments['newState'] === Room::READ_WRITE) {
$listener->sendSystemMessage($room, 'read_only_off', $event->getArguments());
}
});

$dispatcher->addListener(Room::class . '::postAddUsers', function(GenericEvent $event) {
$participants = $event->getArgument('users');
Expand Down
Loading

0 comments on commit ca6e9fe

Please sign in to comment.