Merge pull request #16828 from nextcloud/feature/noid/accept-incoming-shares

🔗☑️ Accept all incoming shares
This commit is contained in:
Roeland Jago Douma 2019-11-18 20:11:20 +01:00 committed by GitHub
commit ccc0a5e0d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 663 additions and 38 deletions

View File

@ -86,7 +86,7 @@ class Notifier implements INotifier {
* @throws \InvalidArgumentException
*/
public function prepare(INotification $notification, string $languageCode): INotification {
if ($notification->getApp() !== 'files_sharing') {
if ($notification->getApp() !== 'files_sharing' || $notification->getObjectType() !== 'remote_share') {
// Not my app => throw
throw new \InvalidArgumentException();
}

View File

@ -38,6 +38,7 @@ use OCA\Files_Sharing\AppInfo\Application;
$application = \OC::$server->query(Application::class);
$application->registerMountProviders();
$application->register();
$eventDispatcher = \OC::$server->getEventDispatcher();
$eventDispatcher->addListener(

View File

@ -9,7 +9,7 @@
Turning the feature off removes shared files and folders on the server for all share recipients, and also on the sync clients and mobile apps. More information is available in the Nextcloud Documentation.
</description>
<version>1.10.0</version>
<version>1.10.1</version>
<licence>agpl</licence>
<author>Michael Gapczynski</author>
<author>Bjoern Schiessle</author>
@ -36,6 +36,7 @@ Turning the feature off removes shared files and folders on the server for all s
<post-migration>
<step>OCA\Files_Sharing\Migration\OwncloudGuestShareType</step>
<step>OCA\Files_Sharing\Migration\SetPasswordColumn</step>
<step>OCA\Files_Sharing\Migration\SetAcceptedStatus</step>
</post-migration>
</repair-steps>

View File

@ -73,6 +73,11 @@ return [
'url' => '/api/v1/shares/{id}',
'verb' => 'DELETE',
],
[
'name' => 'ShareAPI#acceptShare',
'url' => '/api/v1/shares/pending/{id}',
'verb' => 'POST',
],
/*
* Deleted Shares
*/

View File

@ -49,8 +49,10 @@ return array(
'OCA\\Files_Sharing\\Middleware\\ShareInfoMiddleware' => $baseDir . '/../lib/Middleware/ShareInfoMiddleware.php',
'OCA\\Files_Sharing\\Middleware\\SharingCheckMiddleware' => $baseDir . '/../lib/Middleware/SharingCheckMiddleware.php',
'OCA\\Files_Sharing\\Migration\\OwncloudGuestShareType' => $baseDir . '/../lib/Migration/OwncloudGuestShareType.php',
'OCA\\Files_Sharing\\Migration\\SetAcceptedStatus' => $baseDir . '/../lib/Migration/SetAcceptedStatus.php',
'OCA\\Files_Sharing\\Migration\\SetPasswordColumn' => $baseDir . '/../lib/Migration/SetPasswordColumn.php',
'OCA\\Files_Sharing\\MountProvider' => $baseDir . '/../lib/MountProvider.php',
'OCA\\Files_Sharing\\Notification\\Listener' => $baseDir . '/../lib/Notification/Listener.php',
'OCA\\Files_Sharing\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php',
'OCA\\Files_Sharing\\Scanner' => $baseDir . '/../lib/Scanner.php',
'OCA\\Files_Sharing\\ShareBackend\\File' => $baseDir . '/../lib/ShareBackend/File.php',

View File

@ -64,8 +64,10 @@ class ComposerStaticInitFiles_Sharing
'OCA\\Files_Sharing\\Middleware\\ShareInfoMiddleware' => __DIR__ . '/..' . '/../lib/Middleware/ShareInfoMiddleware.php',
'OCA\\Files_Sharing\\Middleware\\SharingCheckMiddleware' => __DIR__ . '/..' . '/../lib/Middleware/SharingCheckMiddleware.php',
'OCA\\Files_Sharing\\Migration\\OwncloudGuestShareType' => __DIR__ . '/..' . '/../lib/Migration/OwncloudGuestShareType.php',
'OCA\\Files_Sharing\\Migration\\SetAcceptedStatus' => __DIR__ . '/..' . '/../lib/Migration/SetAcceptedStatus.php',
'OCA\\Files_Sharing\\Migration\\SetPasswordColumn' => __DIR__ . '/..' . '/../lib/Migration/SetPasswordColumn.php',
'OCA\\Files_Sharing\\MountProvider' => __DIR__ . '/..' . '/../lib/MountProvider.php',
'OCA\\Files_Sharing\\Notification\\Listener' => __DIR__ . '/..' . '/../lib/Notification/Listener.php',
'OCA\\Files_Sharing\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php',
'OCA\\Files_Sharing\\Scanner' => __DIR__ . '/..' . '/../lib/Scanner.php',
'OCA\\Files_Sharing\\ShareBackend\\File' => __DIR__ . '/..' . '/../lib/ShareBackend/File.php',

View File

@ -32,6 +32,7 @@ namespace OCA\Files_Sharing\AppInfo;
use OCA\Files_Sharing\Middleware\OCSShareAPIMiddleware;
use OCA\Files_Sharing\Middleware\ShareInfoMiddleware;
use OCA\Files_Sharing\MountProvider;
use OCA\Files_Sharing\Notification\Listener;
use OCA\Files_Sharing\Notification\Notifier;
use OCP\AppFramework\App;
use OC\AppFramework\Utility\SimpleContainer;
@ -42,9 +43,11 @@ use OCP\AppFramework\Utility\IControllerMethodReflector;
use OCP\Defaults;
use OCP\Federation\ICloudIdManager;
use \OCP\IContainer;
use OCP\IGroup;
use OCP\IServerContainer;
use OCA\Files_Sharing\Capabilities;
use OCA\Files_Sharing\External\Manager;
use Symfony\Component\EventDispatcher\GenericEvent;
class Application extends App {
public function __construct(array $urlParams = array()) {
@ -178,4 +181,18 @@ class Application extends App {
$mountProviderCollection->registerProvider($this->getContainer()->query('MountProvider'));
$mountProviderCollection->registerProvider($this->getContainer()->query('ExternalMountProvider'));
}
public function register(): void {
$dispatcher = $this->getContainer()->getServer()->getEventDispatcher();
$dispatcher->addListener('OCP\Share::postShare', function(GenericEvent $event) {
/** @var Listener $listener */
$listener = $this->getContainer()->query(Listener::class);
$listener->shareNotification($event);
});
$dispatcher->addListener(IGroup::class . '::postAddUser', function(GenericEvent $event) {
/** @var Listener $listener */
$listener = $this->getContainer()->query(Listener::class);
$listener->userAddedToGroup($event);
});
}
}

View File

@ -946,6 +946,38 @@ class ShareAPIController extends OCSController {
return new DataResponse($this->formatShare($share));
}
/**
* @NoAdminRequired
*
* @param string $id
* @return DataResponse
* @throws OCSNotFoundException
* @throws OCSException
* @throws OCSBadRequestException
*/
public function acceptShare(string $id): DataResponse {
try {
$share = $this->getShareById($id);
} catch (ShareNotFound $e) {
throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
}
if (!$this->canAccessShare($share)) {
throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist'));
}
try {
$this->shareManager->acceptShare($share, $this->currentUser);
} catch (GenericShareException $e) {
$code = $e->getCode() === 0 ? 403 : $e->getCode();
throw new OCSException($e->getHint(), $code);
} catch (\Exception $e) {
throw new OCSBadRequestException($e->getMessage(), $e);
}
return new DataResponse();
}
/**
* Does the user have read permission on the share
*
@ -1078,8 +1110,8 @@ class ShareAPIController extends OCSController {
* @suppress PhanUndeclaredClassMethod
*/
protected function canDeleteShareFromSelf(\OCP\Share\IShare $share): bool {
if ($share->getShareType() !== Share::SHARE_TYPE_GROUP &&
$share->getShareType() !== Share::SHARE_TYPE_ROOM
if ($share->getShareType() !== IShare::TYPE_GROUP &&
$share->getShareType() !== IShare::TYPE_ROOM
) {
return false;
}

View File

@ -44,6 +44,7 @@ use OCP\IUserManager;
use OCP\Notification\IManager;
use OCP\OCS\IDiscoveryService;
use OCP\Share;
use OCP\Share\IShare;
class Manager {
const STORAGE = '\OCA\Files_Sharing\External\Storage';
@ -151,10 +152,10 @@ class Manager {
public function addShare($remote, $token, $password, $name, $owner, $shareType, $accepted=false, $user = null, $remoteId = -1, $parent = -1) {
$user = $user ? $user : $this->uid;
$accepted = $accepted ? 1 : 0;
$accepted = $accepted ? IShare::STATUS_ACCEPTED : IShare::STATUS_PENDING;
$name = Filesystem::normalizePath('/' . $name);
if (!$accepted) {
if ($accepted !== IShare::STATUS_ACCEPTED) {
// To avoid conflicts with the mount point generation later,
// we only use a temporary mount point name here. The real
// mount point name will be generated when accepting the share,

View File

@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Joas Schilling <coding@schilljs.com>
*
* @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\Files_Sharing\Migration;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
use OCP\Share\IShare;
class SetAcceptedStatus implements IRepairStep {
/** @var IDBConnection */
private $connection;
/** @var IConfig */
private $config;
public function __construct(IDBConnection $connection, IConfig $config) {
$this->connection = $connection;
$this->config = $config;
}
/**
* Returns the step's name
*
* @return string
* @since 9.1.0
*/
public function getName(): string {
return 'Set existing shares as accepted';
}
/**
* @param IOutput $output
*/
public function run(IOutput $output): void {
if (!$this->shouldRun()) {
return;
}
$query = $this->connection->getQueryBuilder();
$query
->update('share')
->set('accepted', $query->createNamedParameter(IShare::STATUS_ACCEPTED))
->where($query->expr()->in('share_type', $query->createNamedParameter([IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_USERGROUP], IQueryBuilder::PARAM_INT_ARRAY)));
$query->execute();
}
protected function shouldRun() {
$appVersion = $this->config->getAppValue('files_sharing', 'installed_version', '0.0.0');
return version_compare($appVersion, '1.10.1', '<');
}
}

View File

@ -35,6 +35,7 @@ use OCP\IConfig;
use OCP\ILogger;
use OCP\IUser;
use OCP\Share\IManager;
use OCP\Share\IShare;
class MountProvider implements IMountProvider {
/**
@ -94,6 +95,14 @@ class MountProvider implements IMountProvider {
try {
/** @var \OCP\Share\IShare $parentShare */
$parentShare = $share[0];
if ($parentShare->getStatus() !== IShare::STATUS_ACCEPTED &&
($parentShare->getShareType() === IShare::TYPE_GROUP ||
$parentShare->getShareType() === IShare::TYPE_USERGROUP ||
$parentShare->getShareType() === IShare::TYPE_USER)) {
continue;
}
$owner = $parentShare->getShareOwner();
if (!isset($ownerViews[$owner])) {
$ownerViews[$owner] = new View('/' . $parentShare->getShareOwner() . '/files');
@ -184,12 +193,16 @@ class MountProvider implements IMountProvider {
$superShare->setId($shares[0]->getId())
->setShareOwner($shares[0]->getShareOwner())
->setNodeId($shares[0]->getNodeId())
->setShareType($shares[0]->getShareType())
->setTarget($shares[0]->getTarget());
// use most permissive permissions
$permissions = 0;
$status = IShare::STATUS_PENDING;
foreach ($shares as $share) {
$permissions |= $share->getPermissions();
$status = max($status, $share->getStatus());
if ($share->getTarget() !== $superShare->getTarget()) {
// adjust target, for database consistency
$share->setTarget($superShare->getTarget());
@ -216,7 +229,8 @@ class MountProvider implements IMountProvider {
}
}
$superShare->setPermissions($permissions);
$superShare->setPermissions($permissions)
->setStatus($status);
$result[] = [$superShare, $shares];
}

View File

@ -0,0 +1,131 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Joas Schilling <coding@schilljs.com>
*
* @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\Files_Sharing\Notification;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IUser;
use OCP\Notification\IManager as INotificationManager;
use OCP\Notification\INotification;
use OCP\Share\IManager as IShareManager;
use OCP\Share\IShare;
use Symfony\Component\EventDispatcher\GenericEvent;
class Listener {
/** @var INotificationManager */
protected $notificationManager;
/** @var IShareManager */
protected $shareManager;
/** @var IGroupManager */
protected $groupManager;
public function __construct(
INotificationManager $notificationManager,
IShareManager $shareManager,
IGroupManager $groupManager
) {
$this->notificationManager = $notificationManager;
$this->shareManager = $shareManager;
$this->groupManager = $groupManager;
}
/**
* @param GenericEvent $event
*/
public function shareNotification(GenericEvent $event): void {
/** @var IShare $share */
$share = $event->getSubject();
$notification = $this->instantiateNotification($share);
if ($share->getShareType() === IShare::TYPE_USER) {
$notification->setSubject(Notifier::INCOMING_USER_SHARE)
->setUser($share->getSharedWith());
$this->notificationManager->notify($notification);
} else if ($share->getShareType() === IShare::TYPE_GROUP) {
$notification->setSubject(Notifier::INCOMING_GROUP_SHARE);
$group = $this->groupManager->get($share->getSharedWith());
foreach ($group->getUsers() as $user) {
if ($user->getUID() === $share->getShareOwner() ||
$user->getUID() === $share->getSharedBy()) {
continue;
}
$notification->setUser($user->getUID());
$this->notificationManager->notify($notification);
}
}
}
/**
* @param GenericEvent $event
*/
public function userAddedToGroup(GenericEvent $event): void {
/** @var IGroup $group */
$group = $event->getSubject();
/** @var IUser $user */
$user = $event->getArgument('user');
$offset = 0;
while (true) {
$shares = $this->shareManager->getSharedWith($user->getUID(), IShare::TYPE_GROUP, null, 50, $offset);
if (empty($shares)) {
break;
}
foreach ($shares as $share) {
if ($share->getSharedWith() !== $group->getGID()) {
continue;
}
if ($user->getUID() === $share->getShareOwner() ||
$user->getUID() === $share->getSharedBy()) {
continue;
}
$notification = $this->instantiateNotification($share);
$notification->setSubject(Notifier::INCOMING_GROUP_SHARE)
->setUser($user->getUID());
$this->notificationManager->notify($notification);
}
$offset += 50;
}
}
/**
* @param IShare $share
* @return INotification
*/
protected function instantiateNotification(IShare $share): INotification {
$notification = $this->notificationManager->createNotification();
$notification
->setApp('files_sharing')
->setObject('share', $share->getFullId())
->setDateTime($share->getShareTime());
return $notification;
}
}

View File

@ -2,8 +2,10 @@
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
* @copyright Copyright (c) 2019, Joas Schilling <coding@schilljs.com>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
@ -25,43 +27,83 @@ declare(strict_types=1);
namespace OCA\Files_Sharing\Notification;
use OCP\Files\IRootFolder;
use OCP\IL10N;
use OCP\IGroupManager;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserManager;
use OCP\L10N\IFactory;
use OCP\Notification\AlreadyProcessedException;
use OCP\Notification\INotification;
use OCP\Notification\INotifier;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
use OCP\Share\IShare;
class Notifier implements INotifier {
public const INCOMING_USER_SHARE = 'incoming_user_share';
public const INCOMING_GROUP_SHARE = 'incoming_group_share';
/** @var IFactory */
protected $l10nFactory;
/** @var IManager */
private $shareManager;
/** @var IRootFolder */
private $rootFolder;
/** @var IGroupManager */
protected $groupManager;
/** @var IUserManager */
protected $userManager;
/** @var IURLGenerator */
protected $url;
public function __construct(IFactory $l10nFactory,
IManager $shareManager,
IRootFolder $rootFolder) {
IRootFolder $rootFolder,
IGroupManager $groupManager,
IUserManager $userManager,
IURLGenerator $url) {
$this->l10nFactory = $l10nFactory;
$this->shareManager = $shareManager;
$this->rootFolder = $rootFolder;
$this->groupManager = $groupManager;
$this->userManager = $userManager;
$this->url = $url;
}
/**
* Identifier of the notifier, only use [a-z0-9_]
*
* @return string
* @since 17.0.0
*/
public function getID(): string {
return 'files_sharing';
}
/**
* Human readable name describing the notifier
*
* @return string
* @since 17.0.0
*/
public function getName(): string {
return $this->l10nFactory->get('files_sharing')->t('Files sharing');
return $this->l10nFactory->get('files_sharing')->t('File sharing');
}
/**
* @param INotification $notification
* @param string $languageCode The code of the language that should be used to prepare the notification
* @return INotification
* @throws \InvalidArgumentException When the notification was not prepared by a notifier
* @throws AlreadyProcessedException When the notification is not needed anymore and should be deleted
* @since 9.0.0
*/
public function prepare(INotification $notification, string $languageCode): INotification {
if ($notification->getApp() !== 'files_sharing' ||
$notification->getSubject() !== 'expiresTomorrow') {
($notification->getSubject() !== 'expiresTomorrow' &&
$notification->getObjectType() !== 'share')) {
throw new \InvalidArgumentException('Unhandled app or subject');
}
@ -74,6 +116,15 @@ class Notifier implements INotifier {
throw new AlreadyProcessedException();
}
if ($notification->getSubject() === 'expiresTomorrow') {
$notification = $this->parseShareExpiration($share, $notification, $l);
} else {
$notification = $this->parseShareInvitation($share, $notification, $l);
}
return $notification;
}
protected function parseShareExpiration(IShare $share, INotification $notification, IL10N $l): INotification {
$node = $share->getNode();
$userFolder = $this->rootFolder->getUserFolder($notification->getUser());
$path = $userFolder->getRelativePath($node->getPath());
@ -95,4 +146,112 @@ class Notifier implements INotifier {
return $notification;
}
protected function parseShareInvitation(IShare $share, INotification $notification, IL10N $l): INotification {
if ($share->getShareType() === IShare::TYPE_USER) {
if ($share->getStatus() !== IShare::STATUS_PENDING) {
throw new AlreadyProcessedException();
}
} else if ($share->getShareType() === IShare::TYPE_GROUP) {
if ($share->getStatus() !== IShare::STATUS_PENDING) {
throw new AlreadyProcessedException();
}
}
switch ($notification->getSubject()) {
case self::INCOMING_USER_SHARE:
if ($share->getSharedWith() !== $notification->getUser()) {
throw new AlreadyProcessedException();
}
$sharer = $this->userManager->get($share->getSharedBy());
if (!$sharer instanceof IUser) {
throw new \InvalidArgumentException('Temporary failure');
}
$subject = $l->t('You received {share} as a share by {user}');
$subjectParameters = [
'share' => [
'type' => 'highlight',
'id' => $notification->getObjectId(),
'name' => $share->getNode()->getName(),
],
'user' => [
'type' => 'user',
'id' => $sharer->getUID(),
'name' => $sharer->getDisplayName(),
],
];
break;
case self::INCOMING_GROUP_SHARE:
$user = $this->userManager->get($notification->getUser());
if (!$user instanceof IUser) {
throw new AlreadyProcessedException();
}
$group = $this->groupManager->get($share->getSharedWith());
if (!$group->inGroup($user)) {
throw new AlreadyProcessedException();
}
if ($share->getPermissions() === 0) {
// Already rejected
throw new AlreadyProcessedException();
}
$sharer = $this->userManager->get($share->getSharedBy());
if (!$sharer instanceof IUser) {
throw new \InvalidArgumentException('Temporary failure');
}
$subject = $l->t('You received {share} to group {group} as a share by {user}');
$subjectParameters = [
'share' => [
'type' => 'highlight',
'id' => $notification->getObjectId(),
'name' => $share->getNode()->getName(),
],
'group' => [
'type' => 'user-group',
'id' => $group->getGID(),
'name' => $group->getDisplayName(),
],
'user' => [
'type' => 'user',
'id' => $sharer->getUID(),
'name' => $sharer->getDisplayName(),
],
];
break;
default:
throw new \InvalidArgumentException('Invalid subject');
}
$placeholders = $replacements = [];
foreach ($subjectParameters as $placeholder => $parameter) {
$placeholders[] = '{' . $placeholder . '}';
$replacements[] = $parameter['name'];
}
$notification->setParsedSubject(str_replace($placeholders, $replacements, $subject))
->setRichSubject($subject, $subjectParameters)
->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg')));
$acceptAction = $notification->createAction();
$acceptAction->setParsedLabel($l->t('Accept'))
->setLink($this->url->linkToOCSRouteAbsolute('files_sharing.ShareAPI.acceptShare', ['id' => $share->getId()]), 'POST')
->setPrimary(true);
$notification->addParsedAction($acceptAction);
$rejectAction = $notification->createAction();
$rejectAction->setParsedLabel($l->t('Reject'))
->setLink($this->url->linkToOCSRouteAbsolute('files_sharing.ShareAPI.deleteShare', ['id' => $share->getId()]), 'DELETE')
->setPrimary(false);
$notification->addParsedAction($rejectAction);
return $notification;
}
}

View File

@ -142,6 +142,7 @@ class DefaultShareProvider implements IShareProvider {
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
//Set the UID of the user we share with
$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
$qb->setValue('accepted', $qb->createNamedParameter(IShare::STATUS_PENDING));
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
//Set the GID of the group we share with
$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
@ -252,6 +253,7 @@ class DefaultShareProvider implements IShareProvider {
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
->set('note', $qb->createNamedParameter($share->getNote()))
->set('accepted', $qb->createNamedParameter($share->getStatus()))
->execute();
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
$qb = $this->dbConn->getQueryBuilder();
@ -318,6 +320,72 @@ class DefaultShareProvider implements IShareProvider {
return $share;
}
/**
* Accept a share.
*
* @param IShare $share
* @param string $recipient
* @return IShare The share object
* @since 9.0.0
*/
public function acceptShare(IShare $share, string $recipient): IShare {
if ($share->getShareType() === IShare::TYPE_GROUP) {
$group = $this->groupManager->get($share->getSharedWith());
$user = $this->userManager->get($recipient);
if (is_null($group)) {
throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist');
}
if (!$group->inGroup($user)) {
throw new ProviderException('Recipient not in receiving group');
}
// Try to fetch user specific share
$qb = $this->dbConn->getQueryBuilder();
$stmt = $qb->select('*')
->from('share')
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)))
->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
->andWhere($qb->expr()->orX(
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
))
->execute();
$data = $stmt->fetch();
$stmt->closeCursor();
/*
* Check if there already is a user specific group share.
* If there is update it (if required).
*/
if ($data === false) {
$id = $this->createUserSpecificGroupShare($share, $recipient);
} else {
$id = $data['id'];
}
} else if ($share->getShareType() === IShare::TYPE_USER) {
if ($share->getSharedWith() !== $recipient) {
throw new ProviderException('Recipient does not match');
}
$id = $share->getId();
} else {
throw new ProviderException('Invalid shareType');
}
$qb = $this->dbConn->getQueryBuilder();
$qb->update('share')
->set('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED))
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
->execute();
return $share;
}
/**
* Get all children of this share
* FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
@ -382,13 +450,13 @@ class DefaultShareProvider implements IShareProvider {
* Unshare a share from the recipient. If this is a group share
* this means we need a special entry in the share db.
*
* @param \OCP\Share\IShare $share
* @param IShare $share
* @param string $recipient UserId of recipient
* @throws BackendError
* @throws ProviderException
*/
public function deleteFromSelf(\OCP\Share\IShare $share, $recipient) {
if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
public function deleteFromSelf(IShare $share, $recipient) {
if ($share->getShareType() === IShare::TYPE_GROUP) {
$group = $this->groupManager->get($share->getSharedWith());
$user = $this->userManager->get($recipient);
@ -421,37 +489,23 @@ class DefaultShareProvider implements IShareProvider {
* If there is update it (if required).
*/
if ($data === false) {
$qb = $this->dbConn->getQueryBuilder();
$type = $share->getNodeType();
//Insert new share
$qb->insert('share')
->values([
'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
'share_with' => $qb->createNamedParameter($recipient),
'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
'parent' => $qb->createNamedParameter($share->getId()),
'item_type' => $qb->createNamedParameter($type),
'item_source' => $qb->createNamedParameter($share->getNodeId()),
'file_source' => $qb->createNamedParameter($share->getNodeId()),
'file_target' => $qb->createNamedParameter($share->getTarget()),
'permissions' => $qb->createNamedParameter(0),
'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
])->execute();
} else if ($data['permissions'] !== 0) {
$id = $this->createUserSpecificGroupShare($share, $recipient);
$permissions = $share->getPermissions();
} else {
$permissions = $data['permissions'];
$id = $data['id'];
}
if ($permissions !== 0) {
// Update existing usergroup share
$qb = $this->dbConn->getQueryBuilder();
$qb->update('share')
->set('permissions', $qb->createNamedParameter(0))
->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
->execute();
}
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
} else if ($share->getShareType() === IShare::TYPE_USER) {
if ($share->getSharedWith() !== $recipient) {
throw new ProviderException('Recipient does not match');
@ -464,6 +518,28 @@ class DefaultShareProvider implements IShareProvider {
}
}
protected function createUserSpecificGroupShare(IShare $share, string $recipient): int {
$type = $share->getNodeType();
$qb = $this->dbConn->getQueryBuilder();
$qb->insert('share')
->values([
'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
'share_with' => $qb->createNamedParameter($recipient),
'uid_owner' => $qb->createNamedParameter($share->getShareOwner()),
'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()),
'parent' => $qb->createNamedParameter($share->getId()),
'item_type' => $qb->createNamedParameter($type),
'item_source' => $qb->createNamedParameter($share->getNodeId()),
'file_source' => $qb->createNamedParameter($share->getNodeId()),
'file_target' => $qb->createNamedParameter($share->getTarget()),
'permissions' => $qb->createNamedParameter($share->getPermissions()),
'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
])->execute();
return $qb->getLastInsertId();
}
/**
* @inheritdoc
*
@ -932,6 +1008,7 @@ class DefaultShareProvider implements IShareProvider {
->setTarget($data['file_target'])
->setNote($data['note'])
->setMailSend((bool)$data['mail_send'])
->setStatus((int)$data['accepted'])
->setLabel($data['label']);
$shareTime = new \DateTime();
@ -1020,6 +1097,7 @@ class DefaultShareProvider implements IShareProvider {
while($data = $stmt->fetch()) {
$shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
$shareMap[$data['parent']]->setStatus((int)$data['accepted']);
$shareMap[$data['parent']]->setTarget($data['file_target']);
$shareMap[$data['parent']]->setParent($data['parent']);
}

View File

@ -928,6 +928,30 @@ class Manager implements IManager {
return $share;
}
/**
* Accept a share.
*
* @param IShare $share
* @param string $recipientId
* @return IShare The share object
* @throws \InvalidArgumentException
* @since 9.0.0
*/
public function acceptShare(IShare $share, string $recipientId): IShare {
[$providerId, ] = $this->splitFullId($share->getFullId());
$provider = $this->factory->getProvider($providerId);
if (!method_exists($provider, 'acceptShare')) {
// TODO FIX ME
throw new \InvalidArgumentException('Share provider does not support accepting');
}
$provider->acceptShare($share, $recipientId);
$event = new GenericEvent($share);
$this->eventDispatcher->dispatch('OCP\Share::postAcceptShare', $event);
return $share;
}
/**
* Updates the password of the given share if it is not the same as the
* password of the original share.

View File

@ -58,6 +58,8 @@ class Share implements \OCP\Share\IShare {
private $shareOwner;
/** @var int */
private $permissions;
/** @var int */
private $status;
/** @var string */
private $note = '';
/** @var \DateTime */
@ -318,6 +320,21 @@ class Share implements \OCP\Share\IShare {
return $this->permissions;
}
/**
* @inheritdoc
*/
public function setStatus(int $status): IShare {
$this->status = $status;
return $this;
}
/**
* @inheritdoc
*/
public function getStatus(): int {
return $this->status;
}
/**
* @inheritdoc
*/

View File

@ -54,6 +54,7 @@ interface IManager {
* Update a share.
* The target of the share can't be changed this way: use moveShare
* The share can't be removed this way (permission 0): use deleteShare
* The state can't be changed this way: use acceptShare
*
* @param IShare $share
* @return IShare The share object
@ -62,6 +63,17 @@ interface IManager {
*/
public function updateShare(IShare $share);
/**
* Accept a share.
*
* @param IShare $share
* @param string $recipientId
* @return IShare The share object
* @throws \InvalidArgumentException
* @since 18.0.0
*/
public function acceptShare(IShare $share, string $recipientId): IShare;
/**
* Delete a share
*

View File

@ -49,6 +49,12 @@ interface IShare {
*/
public const TYPE_GROUP = 1;
/**
* @internal
* @since 18.0.0
*/
public const TYPE_USERGROUP = 2;
/**
* @since 17.0.0
*/
@ -96,6 +102,21 @@ interface IShare {
*/
// const TYPE_USERROOM = 11;
/**
* @since 18.0.0
*/
public const STATUS_PENDING = 0;
/**
* @since 18.0.0
*/
public const STATUS_ACCEPTED = 1;
/**
* @since 18.0.0
*/
public const STATUS_REJECTED = 2;
/**
* Set the internal id of the share
* It is only allowed to set the internal id of a share once.
@ -279,6 +300,25 @@ interface IShare {
*/
public function getPermissions();
/**
* Set the accepted status
* See self::STATUS_*
*
* @param int $status
* @return IShare The modified object
* @since 18.0.0
*/
public function setStatus(int $status): IShare;
/**
* Get the accepted status
* See self::STATUS_*
*
* @return int
* @since 18.0.0
*/
public function getStatus(): int;
/**
* Attach a note to a share
*

View File

@ -63,6 +63,16 @@ interface IShareProvider {
*/
public function update(\OCP\Share\IShare $share);
/**
* Accept a share.
*
* @param IShare $share
* @param string $recipient
* @return IShare The share object
* @since 17.0.0
*/
// public function acceptShare(IShare $share, string $recipient): IShare;
/**
* Delete a share
*