зеркало из https://github.com/nextcloud/spreed.git
Merge pull request #1388 from nextcloud/make-rooms-for-files-in-shared-folders-accessible-to-shared-folder-users
Make rooms for files in shared folders accessible to shared folder users
This commit is contained in:
Коммит
6fa076bec9
|
@ -268,12 +268,19 @@
|
|||
padding-right: 0px;
|
||||
}
|
||||
|
||||
#app-sidebar .tabsContainer .tab .app-not-started-placeholder {
|
||||
/* Make the placeholder take the full tab height until the app is
|
||||
* started. */
|
||||
#app-sidebar .tabsContainer .tab .ui-not-ready-placeholder {
|
||||
/* Make the placeholder take the full tab height until the UI is ready. */
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
/* Hide other UI elements when there is a "UI not ready" placeholder. */
|
||||
#app-sidebar .tabsContainer .tab .ui-not-ready-placeholder ~ div,
|
||||
/* #chatView needs to be set explicitly to override the display property of
|
||||
* "#app-sidebar #chatView". */
|
||||
#app-sidebar .tabsContainer .tab .ui-not-ready-placeholder ~ #chatView {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#app-sidebar #chatView .comments {
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
|
|
@ -266,7 +266,7 @@
|
|||
this.listenTo(roomsChannel, 'joinedRoom', this.setActiveRoom);
|
||||
this.listenTo(roomsChannel, 'leaveCurrentRoom', this.setActiveRoom);
|
||||
|
||||
this.$el.append('<div class="app-not-started-placeholder icon-loading"></div>');
|
||||
this.$el.append('<div class="ui-not-ready-placeholder icon-loading"></div>');
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -325,17 +325,29 @@
|
|||
return;
|
||||
}
|
||||
|
||||
if (!OCA.Talk.FilesPlugin.isTalkSidebarSupportedForFile(fileInfo)) {
|
||||
this.model = null;
|
||||
this.$el.prepend('<div class="ui-not-ready-placeholder icon-loading"></div>');
|
||||
|
||||
this._roomForFileModel.leave();
|
||||
OCA.Talk.FilesPlugin.isTalkSidebarSupportedForFile(fileInfo).then(function(supported) {
|
||||
if (supported) {
|
||||
this._setFileInfoWhenTalkSidebarIsSupportedForFile(fileInfo);
|
||||
} else {
|
||||
this._setFileInfoWhenTalkSidebarIsNotSupportedForFile();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
this._renderFileNotSharedUi();
|
||||
_setFileInfoWhenTalkSidebarIsNotSupportedForFile: function() {
|
||||
this.model = null;
|
||||
|
||||
return;
|
||||
}
|
||||
this._roomForFileModel.leave();
|
||||
|
||||
this._renderFileNotSharedUi();
|
||||
},
|
||||
|
||||
_setFileInfoWhenTalkSidebarIsSupportedForFile: function(fileInfo) {
|
||||
if (this.model === fileInfo) {
|
||||
this.$el.find('.ui-not-ready-placeholder').remove();
|
||||
|
||||
// If the tab was hidden and it is being shown again at this
|
||||
// point the tab has not been made visible yet, so the
|
||||
// operations need to be delayed. However, the scroll position
|
||||
|
@ -381,6 +393,18 @@
|
|||
return;
|
||||
}
|
||||
|
||||
// Keep the placeholder visible until the messages for the new room
|
||||
// have been received to prevent showing the messages of the
|
||||
// previous room.
|
||||
// The message collection is updated by the signaling, so there are
|
||||
// no "sync" events to listen to. Moreover, this relies on the fact
|
||||
// that the rooms are never empty (as there will be always at least
|
||||
// a system message for the creation of the room) and thus at least
|
||||
// one model will be always added, triggering the "update" event.
|
||||
OCA.SpreedMe.app._messageCollection.once('update', function() {
|
||||
this.$el.find('.ui-not-ready-placeholder').remove();
|
||||
}, this);
|
||||
|
||||
this._roomForFileModel.join(this.model.get('id'));
|
||||
|
||||
this.$el.find('.file-not-shared').remove();
|
||||
|
@ -443,13 +467,13 @@
|
|||
// Force initial rendering; changes in the room state will
|
||||
// automatically render the button again from now on.
|
||||
this._callButton.render();
|
||||
this._callButton.$el.prependTo(this.$el);
|
||||
this._callButton.$el.insertBefore(OCA.SpreedMe.app._chatView.$el);
|
||||
},
|
||||
|
||||
setAppStarted: function() {
|
||||
this._appStarted = true;
|
||||
|
||||
this.$el.find('.app-not-started-placeholder').remove();
|
||||
this.$el.find('.ui-not-ready-placeholder').remove();
|
||||
|
||||
// Set again the file info now that the app has started.
|
||||
if (this.model !== null) {
|
||||
|
@ -507,30 +531,52 @@
|
|||
},
|
||||
|
||||
/**
|
||||
* Returns whether the Talk tab can be displayed for the file.
|
||||
* Returns whether the Talk sidebar is supported for the file or not.
|
||||
*
|
||||
* @return True if the file is shared with the current user or by the
|
||||
* current user to another user (as a user, group...), false
|
||||
* otherwise.
|
||||
* In some cases it is not possible to know if the Talk sidebar is
|
||||
* supported for the file or not just from the data in the FileInfo (for
|
||||
* example, for files in a folder shared by the current user). Due to
|
||||
* that a Promise is always returned; the Promise will be resolved as
|
||||
* soon as possible (in some cases, immediately) with either true or
|
||||
* false, depending on whether the Talk sidebar is supported for the
|
||||
* file or not.
|
||||
*
|
||||
* The Talk sidebar is supported for a file if the file is shared with
|
||||
* the current user or by the current user to another user (as a user,
|
||||
* group...), or if the file is a descendant of a folder that meets
|
||||
* those conditions.
|
||||
*
|
||||
* @param {OCA.Files.FileInfo}
|
||||
* @return {Promise}
|
||||
*/
|
||||
isTalkSidebarSupportedForFile: function(fileInfo) {
|
||||
var deferred = $.Deferred();
|
||||
|
||||
if (!fileInfo) {
|
||||
return false;
|
||||
deferred.resolve(false);
|
||||
|
||||
return deferred.promise();
|
||||
}
|
||||
|
||||
if (fileInfo.get('type') === 'dir') {
|
||||
return false;
|
||||
deferred.resolve(false);
|
||||
|
||||
return deferred.promise();
|
||||
}
|
||||
|
||||
if (fileInfo.get('shareOwnerId')) {
|
||||
// Shared with me
|
||||
// TODO How to check that it is not a remote share? At least for
|
||||
// local shares "shareTypes" is not defined when shared with me.
|
||||
return true;
|
||||
deferred.resolve(true);
|
||||
|
||||
return deferred.promise();
|
||||
}
|
||||
|
||||
if (!fileInfo.get('shareTypes')) {
|
||||
return false;
|
||||
OCA.Talk.FilesPlugin._isRoomForFileAccessible(fileInfo.id, deferred);
|
||||
|
||||
return deferred.promise();
|
||||
}
|
||||
|
||||
var shareTypes = fileInfo.get('shareTypes').filter(function(shareType) {
|
||||
|
@ -544,10 +590,41 @@
|
|||
});
|
||||
|
||||
if (shareTypes.length === 0) {
|
||||
return false;
|
||||
OCA.Talk.FilesPlugin._isRoomForFileAccessible(fileInfo.id, deferred);
|
||||
|
||||
return deferred.promise();
|
||||
}
|
||||
|
||||
return true;
|
||||
deferred.resolve(true);
|
||||
|
||||
return deferred.promise();
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolves the Deferred with whether the room for the given file ID is
|
||||
* accessible or not.
|
||||
*
|
||||
* When it is not possible to know whether the Talk sidebar is supported
|
||||
* for a file or not only from the data in the FileInfo it is necessary
|
||||
* to query the server.
|
||||
*
|
||||
* @param {string} fileId
|
||||
* @param {Deferred} deferred
|
||||
*/
|
||||
_isRoomForFileAccessible: function(fileId, deferred) {
|
||||
$.ajax({
|
||||
url: OC.linkToOCS('apps/spreed/api/v1', 2) + 'file/' + fileId,
|
||||
type: 'GET',
|
||||
beforeSend: function(request) {
|
||||
request.setRequestHeader('Accept', 'application/json');
|
||||
},
|
||||
success: function() {
|
||||
deferred.resolve(true);
|
||||
},
|
||||
error: function() {
|
||||
deferred.resolve(false);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setupSignalingEventHandlers: function() {
|
||||
|
|
|
@ -31,8 +31,10 @@ use OCA\Spreed\Manager;
|
|||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\OCS\OCSNotFoundException;
|
||||
use OCP\AppFramework\OCSController;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\IL10N;
|
||||
use OCP\IRequest;
|
||||
use OCP\Share\IShare;
|
||||
|
||||
class FilesController extends OCSController {
|
||||
|
||||
|
@ -84,12 +86,14 @@ class FilesController extends OCSController {
|
|||
* In any case, to create or even get the token of the room, the file must
|
||||
* be shared and the user must have direct access to that file; an error
|
||||
* is returned otherwise. A user has direct access to a file if she has
|
||||
* access to it through a user, group, circle or room share (but not through
|
||||
* a link share, for example), or if she is the owner of such a file.
|
||||
* access to it (or to an ancestor) through a user, group, circle or room
|
||||
* share (but not through a link share, for example), or if she is the owner
|
||||
* of such a file.
|
||||
*
|
||||
* @param string $fileId
|
||||
* @return DataResponse the status code is "200 OK" if a room is returned,
|
||||
* or "404 Not found" if the given file id was invalid.
|
||||
* @throws OCSNotFoundException
|
||||
*/
|
||||
public function getRoom(string $fileId): DataResponse {
|
||||
$share = $this->util->getAnyDirectShareOfFileAccessibleByUser($fileId, $this->currentUser);
|
||||
|
@ -100,8 +104,12 @@ class FilesController extends OCSController {
|
|||
try {
|
||||
$room = $this->manager->getRoomByObject('file', $fileId);
|
||||
} catch (RoomNotFoundException $e) {
|
||||
$node = $share->getNode();
|
||||
$room = $this->manager->createPublicRoom($node->getName(), 'file', $fileId);
|
||||
try {
|
||||
$name = $this->getFileName($share, $fileId);
|
||||
} catch (NotFoundException $e) {
|
||||
throw new OCSNotFoundException($this->l->t('File is not shared, or shared but not with the user'));
|
||||
}
|
||||
$room = $this->manager->createPublicRoom($name, 'file', $fileId);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -115,4 +123,32 @@ class FilesController extends OCSController {
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the file in the share.
|
||||
*
|
||||
* If the given share itself is a file its name is returned; otherwise the
|
||||
* file is looked for in the given shared folder and its name is returned.
|
||||
*
|
||||
* @param IShare $share
|
||||
* @param string $fileId
|
||||
* @return string
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
private function getFileName(IShare $share, string $fileId): string {
|
||||
$node = $share->getNode();
|
||||
|
||||
if ($node->getType() === \OCP\Files\FileInfo::TYPE_FILE) {
|
||||
return $node->getName();
|
||||
}
|
||||
|
||||
$fileById = $node->getById($fileId);
|
||||
|
||||
if (empty($fileById)) {
|
||||
throw new NotFoundException('File not found in share');
|
||||
}
|
||||
|
||||
$file = array_shift($fileById);
|
||||
return $file->getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,9 +36,9 @@ use Symfony\Component\EventDispatcher\GenericEvent;
|
|||
* specific shared file, for example, when collaboratively editing it. The room
|
||||
* is persistent and can be accessed simultaneously by any user with direct
|
||||
* access (user, group, circle and room share, but not link share, for example)
|
||||
* to that file. The room has no owner, although self joined users become
|
||||
* persistent participants automatically when they join until they explicitly
|
||||
* leave or no longer have access to the file.
|
||||
* to that file (or to an ancestor). The room has no owner, although self joined
|
||||
* users become persistent participants automatically when they join until they
|
||||
* explicitly leave or no longer have access to the file.
|
||||
*
|
||||
* These rooms are associated to a "file" object, and their custom behaviour is
|
||||
* provided by calling the methods of this class as a response to different room
|
||||
|
@ -84,9 +84,9 @@ class Listener {
|
|||
* Prevents users from joining if they do not have direct access to the
|
||||
* file.
|
||||
*
|
||||
* A user has direct access to a file if she received the file through a
|
||||
* user, group, circle or room share (but not through a link share, for
|
||||
* example), or if she is the owner of such a file.
|
||||
* A user has direct access to a file if she received the file (or an
|
||||
* ancestor) through a user, group, circle or room share (but not through a
|
||||
* link share, for example), or if she is the owner of such a file.
|
||||
*
|
||||
* This method should be called before a user joins a room.
|
||||
*
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace OCA\Spreed\Files;
|
|||
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\Node;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Share\IManager as IShareManager;
|
||||
use OCP\Share\IShare;
|
||||
|
||||
|
@ -77,6 +78,8 @@ class Util {
|
|||
* A user has direct access to a share and, thus, to a file, if she received
|
||||
* the file through a user, group, circle or room share (but not through a
|
||||
* public link, for example), or if she is the owner of such a share.
|
||||
* Note that this includes too files received as a descendant of a folder
|
||||
* that meets the above conditions.
|
||||
*
|
||||
* Only files are taken into account; folders are ignored.
|
||||
*
|
||||
|
@ -86,16 +89,27 @@ class Util {
|
|||
*/
|
||||
public function getAnyDirectShareOfFileAccessibleByUser(string $fileId, string $userId) {
|
||||
$userFolder = $this->rootFolder->getUserFolder($userId);
|
||||
$fileById = $userFolder->getById($fileId);
|
||||
if (empty($fileById)) {
|
||||
$nodes = $userFolder->getById($fileId);
|
||||
if (empty($nodes)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($fileById as $node) {
|
||||
$nodes = array_filter($nodes, function($node) {
|
||||
return $node->getType() === \OCP\Files\FileInfo::TYPE_FILE;
|
||||
});
|
||||
|
||||
while (!empty($nodes)) {
|
||||
$node = array_pop($nodes);
|
||||
|
||||
$share = $this->getAnyDirectShareOfNodeAccessibleByUser($node, $userId);
|
||||
if ($share) {
|
||||
return $share;
|
||||
}
|
||||
|
||||
try {
|
||||
$nodes[] = $node->getParent();
|
||||
} catch (NotFoundException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -104,17 +118,11 @@ class Util {
|
|||
/**
|
||||
* Returns any share of the node that the user has direct access to.
|
||||
*
|
||||
* Only files are taken into account; folders are ignored.
|
||||
*
|
||||
* @param Node $node
|
||||
* @param string $userId
|
||||
* @return IShare|null
|
||||
*/
|
||||
private function getAnyDirectShareOfNodeAccessibleByUser(Node $node, string $userId) {
|
||||
if ($node->getType() === \OCP\Files\FileInfo::TYPE_FOLDER) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$reshares = false;
|
||||
$limit = 1;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче