mirror of
https://github.com/nextcloud/server.git
synced 2026-06-11 01:30:50 -04:00
chore(files_sharing): refactor ShareAPIController shared methods
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
This commit is contained in:
parent
06069e56ee
commit
fc3ae5ca4b
26 changed files with 884 additions and 1109 deletions
|
|
@ -35,6 +35,7 @@ return array(
|
|||
'OCA\\Files_Sharing\\Controller\\RemoteController' => $baseDir . '/../lib/Controller/RemoteController.php',
|
||||
'OCA\\Files_Sharing\\Controller\\SettingsController' => $baseDir . '/../lib/Controller/SettingsController.php',
|
||||
'OCA\\Files_Sharing\\Controller\\ShareAPIController' => $baseDir . '/../lib/Controller/ShareAPIController.php',
|
||||
'OCA\\Files_Sharing\\Controller\\ShareApiControllerFactory' => $baseDir . '/../lib/Controller/ShareApiControllerFactory.php',
|
||||
'OCA\\Files_Sharing\\Controller\\ShareController' => $baseDir . '/../lib/Controller/ShareController.php',
|
||||
'OCA\\Files_Sharing\\Controller\\ShareInfoController' => $baseDir . '/../lib/Controller/ShareInfoController.php',
|
||||
'OCA\\Files_Sharing\\Controller\\ShareesAPIController' => $baseDir . '/../lib/Controller/ShareesAPIController.php',
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ class ComposerStaticInitFiles_Sharing
|
|||
'OCA\\Files_Sharing\\Controller\\RemoteController' => __DIR__ . '/..' . '/../lib/Controller/RemoteController.php',
|
||||
'OCA\\Files_Sharing\\Controller\\SettingsController' => __DIR__ . '/..' . '/../lib/Controller/SettingsController.php',
|
||||
'OCA\\Files_Sharing\\Controller\\ShareAPIController' => __DIR__ . '/..' . '/../lib/Controller/ShareAPIController.php',
|
||||
'OCA\\Files_Sharing\\Controller\\ShareApiControllerFactory' => __DIR__ . '/..' . '/../lib/Controller/ShareApiControllerFactory.php',
|
||||
'OCA\\Files_Sharing\\Controller\\ShareController' => __DIR__ . '/..' . '/../lib/Controller/ShareController.php',
|
||||
'OCA\\Files_Sharing\\Controller\\ShareInfoController' => __DIR__ . '/..' . '/../lib/Controller/ShareInfoController.php',
|
||||
'OCA\\Files_Sharing\\Controller\\ShareesAPIController' => __DIR__ . '/..' . '/../lib/Controller/ShareesAPIController.php',
|
||||
|
|
|
|||
|
|
@ -15,154 +15,66 @@ use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
|||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\OCS\OCSException;
|
||||
use OCP\AppFramework\OCS\OCSNotFoundException;
|
||||
use OCP\AppFramework\OCSController;
|
||||
use OCP\AppFramework\QueryException;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\IDateTimeZone;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\IPreview;
|
||||
use OCP\IRequest;
|
||||
use OCP\IServerContainer;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Share\Exceptions\GenericShareException;
|
||||
use OCP\Share\Exceptions\ShareNotFound;
|
||||
use OCP\Share\IManager as ShareManager;
|
||||
use OCP\Share\IShare;
|
||||
use OCP\UserStatus\IManager as UserStatusManager;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @psalm-import-type Files_SharingDeletedShare from ResponseDefinitions
|
||||
* @psalm-import-type Files_SharingShare from ResponseDefinitions
|
||||
*/
|
||||
class DeletedShareAPIController extends OCSController {
|
||||
|
||||
/** @var ShareManager */
|
||||
private $shareManager;
|
||||
|
||||
/** @var string */
|
||||
private $userId;
|
||||
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
|
||||
/** @var IGroupManager */
|
||||
private $groupManager;
|
||||
|
||||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
/** @var IAppManager */
|
||||
private $appManager;
|
||||
|
||||
/** @var IServerContainer */
|
||||
private $serverContainer;
|
||||
|
||||
public function __construct(string $appName,
|
||||
class DeletedShareAPIController extends ShareApiControllerFactory {
|
||||
|
||||
public function __construct(
|
||||
IRequest $request,
|
||||
ShareManager $shareManager,
|
||||
string $UserId,
|
||||
IUserManager $userManager,
|
||||
IGroupManager $groupManager,
|
||||
IRootFolder $rootFolder,
|
||||
IAppManager $appManager,
|
||||
IServerContainer $serverContainer) {
|
||||
parent::__construct($appName, $request);
|
||||
|
||||
$this->shareManager = $shareManager;
|
||||
$this->userId = $UserId;
|
||||
$this->userManager = $userManager;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->appManager = $appManager;
|
||||
$this->serverContainer = $serverContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @suppress PhanUndeclaredClassMethod
|
||||
*
|
||||
* @return Files_SharingDeletedShare
|
||||
*/
|
||||
private function formatShare(IShare $share): array {
|
||||
$result = [
|
||||
'id' => $share->getFullId(),
|
||||
'share_type' => $share->getShareType(),
|
||||
'uid_owner' => $share->getSharedBy(),
|
||||
'displayname_owner' => $this->userManager->get($share->getSharedBy())->getDisplayName(),
|
||||
'permissions' => 0,
|
||||
'stime' => $share->getShareTime()->getTimestamp(),
|
||||
'parent' => null,
|
||||
'expiration' => null,
|
||||
'token' => null,
|
||||
'uid_file_owner' => $share->getShareOwner(),
|
||||
'displayname_file_owner' => $this->userManager->get($share->getShareOwner())->getDisplayName(),
|
||||
'path' => $share->getTarget(),
|
||||
];
|
||||
$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
|
||||
$node = $userFolder->getFirstNodeById($share->getNodeId());
|
||||
if (!$node) {
|
||||
// fallback to guessing the path
|
||||
$node = $userFolder->get($share->getTarget());
|
||||
if ($node === null || $share->getTarget() === '') {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
$result['path'] = $userFolder->getRelativePath($node->getPath());
|
||||
if ($node instanceof Folder) {
|
||||
$result['item_type'] = 'folder';
|
||||
} else {
|
||||
$result['item_type'] = 'file';
|
||||
}
|
||||
$result['mimetype'] = $node->getMimetype();
|
||||
$result['storage_id'] = $node->getStorage()->getId();
|
||||
$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
|
||||
$result['item_source'] = $node->getId();
|
||||
$result['file_source'] = $node->getId();
|
||||
$result['file_parent'] = $node->getParent()->getId();
|
||||
$result['file_target'] = $share->getTarget();
|
||||
$result['item_size'] = $node->getSize();
|
||||
$result['item_mtime'] = $node->getMTime();
|
||||
|
||||
$expiration = $share->getExpirationDate();
|
||||
if ($expiration !== null) {
|
||||
$result['expiration'] = $expiration->format('Y-m-d 00:00:00');
|
||||
}
|
||||
|
||||
if ($share->getShareType() === IShare::TYPE_GROUP) {
|
||||
$group = $this->groupManager->get($share->getSharedWith());
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
|
||||
} elseif ($share->getShareType() === IShare::TYPE_ROOM) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = '';
|
||||
|
||||
try {
|
||||
$result = array_merge($result, $this->getRoomShareHelper()->formatShare($share));
|
||||
} catch (QueryException $e) {
|
||||
}
|
||||
} elseif ($share->getShareType() === IShare::TYPE_DECK) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = '';
|
||||
|
||||
try {
|
||||
$result = array_merge($result, $this->getDeckShareHelper()->formatShare($share));
|
||||
} catch (QueryException $e) {
|
||||
}
|
||||
} elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = '';
|
||||
|
||||
try {
|
||||
$result = array_merge($result, $this->getSciencemeshShareHelper()->formatShare($share));
|
||||
} catch (QueryException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
protected ShareManager $shareManager,
|
||||
protected ?string $userId,
|
||||
protected IUserManager $userManager,
|
||||
protected IGroupManager $groupManager,
|
||||
protected IRootFolder $rootFolder,
|
||||
protected IAppManager $appManager,
|
||||
protected ContainerInterface $serverContainer,
|
||||
protected UserStatusManager $userStatusManager,
|
||||
protected IPreview $previewManager,
|
||||
protected IDateTimeZone $dateTimeZone,
|
||||
protected IURLGenerator $urlGenerator,
|
||||
protected IL10N $l,
|
||||
protected LoggerInterface $logger,
|
||||
) {
|
||||
parent::__construct(
|
||||
$request,
|
||||
$shareManager,
|
||||
$userId,
|
||||
$userManager,
|
||||
$groupManager,
|
||||
$rootFolder,
|
||||
$appManager,
|
||||
$serverContainer,
|
||||
$userStatusManager,
|
||||
$previewManager,
|
||||
$dateTimeZone,
|
||||
$urlGenerator,
|
||||
$l,
|
||||
$logger,
|
||||
);
|
||||
$this->isDeletedShareController = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all deleted shares
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, Files_SharingDeletedShare[], array{}>
|
||||
* @return DataResponse<Http::STATUS_OK, Files_SharingShare[], array{}>
|
||||
*
|
||||
* 200: Deleted shares returned
|
||||
*/
|
||||
|
|
@ -212,55 +124,4 @@ class DeletedShareAPIController extends OCSController {
|
|||
|
||||
return new DataResponse([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the helper of DeletedShareAPIController for room shares.
|
||||
*
|
||||
* If the Talk application is not enabled or the helper is not available
|
||||
* a QueryException is thrown instead.
|
||||
*
|
||||
* @return \OCA\Talk\Share\Helper\DeletedShareAPIController
|
||||
* @throws QueryException
|
||||
*/
|
||||
private function getRoomShareHelper() {
|
||||
if (!$this->appManager->isEnabledForUser('spreed')) {
|
||||
throw new QueryException();
|
||||
}
|
||||
|
||||
return $this->serverContainer->get('\OCA\Talk\Share\Helper\DeletedShareAPIController');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the helper of DeletedShareAPIHelper for deck shares.
|
||||
*
|
||||
* If the Deck application is not enabled or the helper is not available
|
||||
* a QueryException is thrown instead.
|
||||
*
|
||||
* @return \OCA\Deck\Sharing\ShareAPIHelper
|
||||
* @throws QueryException
|
||||
*/
|
||||
private function getDeckShareHelper() {
|
||||
if (!$this->appManager->isEnabledForUser('deck')) {
|
||||
throw new QueryException();
|
||||
}
|
||||
|
||||
return $this->serverContainer->get('\OCA\Deck\Sharing\ShareAPIHelper');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the helper of DeletedShareAPIHelper for sciencemesh shares.
|
||||
*
|
||||
* If the sciencemesh application is not enabled or the helper is not available
|
||||
* a QueryException is thrown instead.
|
||||
*
|
||||
* @return \OCA\Deck\Sharing\ShareAPIHelper
|
||||
* @throws QueryException
|
||||
*/
|
||||
private function getSciencemeshShareHelper() {
|
||||
if (!$this->appManager->isEnabledForUser('sciencemesh')) {
|
||||
throw new QueryException();
|
||||
}
|
||||
|
||||
return $this->serverContainer->get('\OCA\ScienceMesh\Sharing\ShareAPIHelper');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,151 +13,63 @@ use OCP\App\IAppManager;
|
|||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\OCSController;
|
||||
use OCP\AppFramework\QueryException;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\IDateTimeZone;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\IPreview;
|
||||
use OCP\IRequest;
|
||||
use OCP\IServerContainer;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Share\IManager as ShareManager;
|
||||
use OCP\Share\IShare;
|
||||
use OCP\UserStatus\IManager as UserStatusManager;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @psalm-import-type Files_SharingDeletedShare from ResponseDefinitions
|
||||
* @psalm-import-type Files_SharingShare from ResponseDefinitions
|
||||
*/
|
||||
class ExpiredShareAPIController extends OCSController {
|
||||
class ExpiredShareAPIController extends ShareApiControllerFactory {
|
||||
|
||||
/** @var ShareManager */
|
||||
private $shareManager;
|
||||
|
||||
/** @var string */
|
||||
private $userId;
|
||||
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
|
||||
/** @var IGroupManager */
|
||||
private $groupManager;
|
||||
|
||||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
/** @var IAppManager */
|
||||
private $appManager;
|
||||
|
||||
/** @var IServerContainer */
|
||||
private $serverContainer;
|
||||
|
||||
public function __construct(string $appName,
|
||||
public function __construct(
|
||||
IRequest $request,
|
||||
ShareManager $shareManager,
|
||||
string $UserId,
|
||||
?string $userId,
|
||||
IUserManager $userManager,
|
||||
IGroupManager $groupManager,
|
||||
IRootFolder $rootFolder,
|
||||
IAppManager $appManager,
|
||||
IServerContainer $serverContainer) {
|
||||
parent::__construct($appName, $request);
|
||||
|
||||
$this->shareManager = $shareManager;
|
||||
$this->userId = $UserId;
|
||||
$this->userManager = $userManager;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->appManager = $appManager;
|
||||
$this->serverContainer = $serverContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @suppress PhanUndeclaredClassMethod
|
||||
*
|
||||
* @return Files_SharingDeletedShare
|
||||
*/
|
||||
private function formatShare(IShare $share): array {
|
||||
$result = [
|
||||
'id' => $share->getFullId(),
|
||||
'share_type' => $share->getShareType(),
|
||||
'uid_owner' => $share->getSharedBy(),
|
||||
'displayname_owner' => $this->userManager->get($share->getSharedBy())->getDisplayName(),
|
||||
'permissions' => 0,
|
||||
'stime' => $share->getShareTime()->getTimestamp(),
|
||||
'parent' => null,
|
||||
'expiration' => null,
|
||||
'token' => null,
|
||||
'uid_file_owner' => $share->getShareOwner(),
|
||||
'displayname_file_owner' => $this->userManager->get($share->getShareOwner())->getDisplayName(),
|
||||
'path' => $share->getTarget(),
|
||||
];
|
||||
$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
|
||||
$node = $userFolder->getFirstNodeById($share->getNodeId());
|
||||
if (!$node) {
|
||||
// fallback to guessing the path
|
||||
$node = $userFolder->get($share->getTarget());
|
||||
if ($node === null || $share->getTarget() === '') {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
$result['path'] = $userFolder->getRelativePath($node->getPath());
|
||||
if ($node instanceof \OCP\Files\Folder) {
|
||||
$result['item_type'] = 'folder';
|
||||
} else {
|
||||
$result['item_type'] = 'file';
|
||||
}
|
||||
$result['mimetype'] = $node->getMimetype();
|
||||
$result['storage_id'] = $node->getStorage()->getId();
|
||||
$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
|
||||
$result['item_source'] = $node->getId();
|
||||
$result['file_source'] = $node->getId();
|
||||
$result['file_parent'] = $node->getParent()->getId();
|
||||
$result['file_target'] = $share->getTarget();
|
||||
$result['item_size'] = $node->getSize();
|
||||
$result['item_mtime'] = $node->getMTime();
|
||||
|
||||
$expiration = $share->getExpirationDate();
|
||||
if ($expiration !== null) {
|
||||
$result['expiration'] = $expiration->format('Y-m-d 00:00:00');
|
||||
}
|
||||
|
||||
if ($share->getShareType() === IShare::TYPE_GROUP) {
|
||||
$group = $this->groupManager->get($share->getSharedWith());
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
|
||||
} elseif ($share->getShareType() === IShare::TYPE_ROOM) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = '';
|
||||
|
||||
try {
|
||||
$result = array_merge($result, $this->getRoomShareHelper()->formatShare($share));
|
||||
} catch (QueryException $e) {
|
||||
}
|
||||
} elseif ($share->getShareType() === IShare::TYPE_DECK) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = '';
|
||||
|
||||
try {
|
||||
$result = array_merge($result, $this->getDeckShareHelper()->formatShare($share));
|
||||
} catch (QueryException $e) {
|
||||
}
|
||||
} elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = '';
|
||||
|
||||
try {
|
||||
$result = array_merge($result, $this->getSciencemeshShareHelper()->formatShare($share));
|
||||
} catch (QueryException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
ContainerInterface $serverContainer,
|
||||
UserStatusManager $userStatusManager,
|
||||
IPreview $previewManager,
|
||||
IDateTimeZone $dateTimeZone,
|
||||
IURLGenerator $urlGenerator,
|
||||
IL10N $l,
|
||||
LoggerInterface $logger,
|
||||
) {
|
||||
parent::__construct(
|
||||
$request,
|
||||
$shareManager,
|
||||
$userId,
|
||||
$userManager,
|
||||
$groupManager,
|
||||
$rootFolder,
|
||||
$appManager,
|
||||
$serverContainer,
|
||||
$userStatusManager,
|
||||
$previewManager,
|
||||
$dateTimeZone,
|
||||
$urlGenerator,
|
||||
$l,
|
||||
$logger,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all expired shares
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, Files_SharingDeletedShare[], array{}>
|
||||
* @return DataResponse<Http::STATUS_OK, Files_SharingShare[], array{}>
|
||||
*
|
||||
* 200: Deleted shares returned
|
||||
*/
|
||||
|
|
@ -181,55 +93,4 @@ class ExpiredShareAPIController extends OCSController {
|
|||
|
||||
return new DataResponse($shares);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the helper of DeletedShareAPIController for room shares.
|
||||
*
|
||||
* If the Talk application is not enabled or the helper is not available
|
||||
* a QueryException is thrown instead.
|
||||
*
|
||||
* @return \OCA\Talk\Share\Helper\DeletedShareAPIController
|
||||
* @throws QueryException
|
||||
*/
|
||||
private function getRoomShareHelper() {
|
||||
if (!$this->appManager->isEnabledForUser('spreed')) {
|
||||
throw new QueryException();
|
||||
}
|
||||
|
||||
return $this->serverContainer->get('\OCA\Talk\Share\Helper\DeletedShareAPIController');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the helper of DeletedShareAPIHelper for deck shares.
|
||||
*
|
||||
* If the Deck application is not enabled or the helper is not available
|
||||
* a QueryException is thrown instead.
|
||||
*
|
||||
* @return \OCA\Deck\Sharing\ShareAPIHelper
|
||||
* @throws QueryException
|
||||
*/
|
||||
private function getDeckShareHelper() {
|
||||
if (!$this->appManager->isEnabledForUser('deck')) {
|
||||
throw new QueryException();
|
||||
}
|
||||
|
||||
return $this->serverContainer->get('\OCA\Deck\Sharing\ShareAPIHelper');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the helper of DeletedShareAPIHelper for sciencemesh shares.
|
||||
*
|
||||
* If the sciencemesh application is not enabled or the helper is not available
|
||||
* a QueryException is thrown instead.
|
||||
*
|
||||
* @return \OCA\Deck\Sharing\ShareAPIHelper
|
||||
* @throws QueryException
|
||||
*/
|
||||
private function getSciencemeshShareHelper() {
|
||||
if (!$this->appManager->isEnabledForUser('sciencemesh')) {
|
||||
throw new QueryException();
|
||||
}
|
||||
|
||||
return $this->serverContainer->get('\OCA\ScienceMesh\Sharing\ShareAPIHelper');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ use OCA\Files_Sharing\Exceptions\SharingRightsException;
|
|||
use OCA\Files_Sharing\External\Storage;
|
||||
use OCA\Files_Sharing\ResponseDefinitions;
|
||||
use OCA\Files_Sharing\SharedStorage;
|
||||
use OCA\GlobalSiteSelector\Service\SlaveService;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
||||
|
|
@ -28,7 +27,6 @@ use OCP\AppFramework\OCS\OCSBadRequestException;
|
|||
use OCP\AppFramework\OCS\OCSException;
|
||||
use OCP\AppFramework\OCS\OCSForbiddenException;
|
||||
use OCP\AppFramework\OCS\OCSNotFoundException;
|
||||
use OCP\AppFramework\OCSController;
|
||||
use OCP\AppFramework\QueryException;
|
||||
use OCP\Constants;
|
||||
use OCP\Files\File;
|
||||
|
|
@ -48,7 +46,6 @@ use OCP\IUserManager;
|
|||
use OCP\Lock\ILockingProvider;
|
||||
use OCP\Lock\LockedException;
|
||||
use OCP\Mail\IMailer;
|
||||
use OCP\Server;
|
||||
use OCP\Share\Exceptions\GenericShareException;
|
||||
use OCP\Share\Exceptions\ShareNotFound;
|
||||
use OCP\Share\IManager;
|
||||
|
|
@ -56,7 +53,6 @@ use OCP\Share\IProviderFactory;
|
|||
use OCP\Share\IShare;
|
||||
use OCP\Share\IShareProviderWithNotification;
|
||||
use OCP\UserStatus\IManager as IUserStatusManager;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
|
|
@ -65,388 +61,47 @@ use Psr\Log\LoggerInterface;
|
|||
*
|
||||
* @psalm-import-type Files_SharingShare from ResponseDefinitions
|
||||
*/
|
||||
class ShareAPIController extends OCSController {
|
||||
class ShareAPIController extends ShareApiControllerFactory {
|
||||
|
||||
private ?Node $lockedNode = null;
|
||||
private string $currentUser;
|
||||
|
||||
/**
|
||||
* Share20OCS constructor.
|
||||
*/
|
||||
public function __construct(
|
||||
string $appName,
|
||||
IRequest $request,
|
||||
private IManager $shareManager,
|
||||
private IGroupManager $groupManager,
|
||||
private IUserManager $userManager,
|
||||
private IRootFolder $rootFolder,
|
||||
private IURLGenerator $urlGenerator,
|
||||
private IL10N $l,
|
||||
private IConfig $config,
|
||||
private IAppManager $appManager,
|
||||
private ContainerInterface $serverContainer,
|
||||
private IUserStatusManager $userStatusManager,
|
||||
private IPreview $previewManager,
|
||||
private IDateTimeZone $dateTimeZone,
|
||||
private LoggerInterface $logger,
|
||||
private IProviderFactory $factory,
|
||||
private IMailer $mailer,
|
||||
?string $userId = null,
|
||||
protected IManager $shareManager,
|
||||
protected IGroupManager $groupManager,
|
||||
protected IUserManager $userManager,
|
||||
protected IRootFolder $rootFolder,
|
||||
protected IURLGenerator $urlGenerator,
|
||||
protected IL10N $l,
|
||||
protected IConfig $config,
|
||||
protected IAppManager $appManager,
|
||||
protected ContainerInterface $serverContainer,
|
||||
protected IUserStatusManager $userStatusManager,
|
||||
protected IPreview $previewManager,
|
||||
protected IDateTimeZone $dateTimeZone,
|
||||
protected LoggerInterface $logger,
|
||||
protected IProviderFactory $factory,
|
||||
protected IMailer $mailer,
|
||||
string $userId,
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->currentUser = $userId;
|
||||
parent::__construct(
|
||||
$request,
|
||||
$shareManager,
|
||||
$userId,
|
||||
$userManager,
|
||||
$groupManager,
|
||||
$rootFolder,
|
||||
$appManager,
|
||||
$serverContainer,
|
||||
$userStatusManager,
|
||||
$previewManager,
|
||||
$dateTimeZone,
|
||||
$urlGenerator,
|
||||
$l,
|
||||
$logger,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an IShare to an array for OCS output
|
||||
*
|
||||
* @param \OCP\Share\IShare $share
|
||||
* @param Node|null $recipientNode
|
||||
* @return Files_SharingShare
|
||||
* @throws NotFoundException In case the node can't be resolved.
|
||||
*
|
||||
* @suppress PhanUndeclaredClassMethod
|
||||
*/
|
||||
protected function formatShare(IShare $share, ?Node $recipientNode = null): array {
|
||||
$sharedBy = $this->userManager->get($share->getSharedBy());
|
||||
$shareOwner = $this->userManager->get($share->getShareOwner());
|
||||
|
||||
$isOwnShare = false;
|
||||
if ($shareOwner !== null) {
|
||||
$isOwnShare = $shareOwner->getUID() === $this->currentUser;
|
||||
}
|
||||
|
||||
$result = [
|
||||
'id' => $share->getId(),
|
||||
'share_type' => $share->getShareType(),
|
||||
'uid_owner' => $share->getSharedBy(),
|
||||
'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
|
||||
// recipient permissions
|
||||
'permissions' => $share->getPermissions(),
|
||||
// current user permissions on this share
|
||||
'can_edit' => $this->canEditShare($share),
|
||||
'can_delete' => $this->canDeleteShare($share),
|
||||
'stime' => $share->getShareTime()->getTimestamp(),
|
||||
'parent' => null,
|
||||
'expiration' => null,
|
||||
'token' => null,
|
||||
'uid_file_owner' => $share->getShareOwner(),
|
||||
'note' => $share->getNote(),
|
||||
'label' => $share->getLabel(),
|
||||
'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
|
||||
];
|
||||
|
||||
$userFolder = $this->rootFolder->getUserFolder($this->currentUser);
|
||||
if ($recipientNode) {
|
||||
$node = $recipientNode;
|
||||
} else {
|
||||
$node = $userFolder->getFirstNodeById($share->getNodeId());
|
||||
if (!$node) {
|
||||
// fallback to guessing the path
|
||||
$node = $userFolder->get($share->getTarget());
|
||||
if ($node === null || $share->getTarget() === '') {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result['path'] = $userFolder->getRelativePath($node->getPath());
|
||||
if ($node instanceof Folder) {
|
||||
$result['item_type'] = 'folder';
|
||||
} else {
|
||||
$result['item_type'] = 'file';
|
||||
}
|
||||
|
||||
// Get the original node permission if the share owner is the current user
|
||||
if ($isOwnShare) {
|
||||
$result['item_permissions'] = $node->getPermissions();
|
||||
}
|
||||
|
||||
// If we're on the recipient side, the node permissions
|
||||
// are bound to the share permissions. So we need to
|
||||
// adjust the permissions to the share permissions if necessary.
|
||||
if (!$isOwnShare) {
|
||||
$result['item_permissions'] = $share->getPermissions();
|
||||
|
||||
// For some reason, single files share are forbidden to have the delete permission
|
||||
// since we have custom methods to check those, let's adjust straight away.
|
||||
// DAV permissions does not have that issue though.
|
||||
if ($this->canDeleteShare($share) || $this->canDeleteShareFromSelf($share)) {
|
||||
$result['item_permissions'] |= Constants::PERMISSION_DELETE;
|
||||
}
|
||||
if ($this->canEditShare($share)) {
|
||||
$result['item_permissions'] |= Constants::PERMISSION_UPDATE;
|
||||
}
|
||||
}
|
||||
|
||||
// See MOUNT_ROOT_PROPERTYNAME dav property
|
||||
$result['is-mount-root'] = $node->getInternalPath() === '';
|
||||
$result['mount-type'] = $node->getMountPoint()->getMountType();
|
||||
|
||||
$result['mimetype'] = $node->getMimetype();
|
||||
$result['has_preview'] = $this->previewManager->isAvailable($node);
|
||||
$result['storage_id'] = $node->getStorage()->getId();
|
||||
$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
|
||||
$result['item_source'] = $node->getId();
|
||||
$result['file_source'] = $node->getId();
|
||||
$result['file_parent'] = $node->getParent()->getId();
|
||||
$result['file_target'] = $share->getTarget();
|
||||
$result['item_size'] = $node->getSize();
|
||||
$result['item_mtime'] = $node->getMTime();
|
||||
|
||||
$expiration = $share->getExpirationDate();
|
||||
if ($expiration !== null) {
|
||||
$expiration->setTimezone($this->dateTimeZone->getTimeZone());
|
||||
$result['expiration'] = $expiration->format('Y-m-d 00:00:00');
|
||||
}
|
||||
|
||||
if ($share->getShareType() === IShare::TYPE_USER) {
|
||||
$sharedWith = $this->userManager->get($share->getSharedWith());
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
|
||||
$result['share_with_displayname_unique'] = $sharedWith !== null ? (
|
||||
!empty($sharedWith->getSystemEMailAddress()) ? $sharedWith->getSystemEMailAddress() : $sharedWith->getUID()
|
||||
) : $share->getSharedWith();
|
||||
|
||||
$userStatuses = $this->userStatusManager->getUserStatuses([$share->getSharedWith()]);
|
||||
$userStatus = array_shift($userStatuses);
|
||||
if ($userStatus) {
|
||||
$result['status'] = [
|
||||
'status' => $userStatus->getStatus(),
|
||||
'message' => $userStatus->getMessage(),
|
||||
'icon' => $userStatus->getIcon(),
|
||||
'clearAt' => $userStatus->getClearAt()
|
||||
? (int)$userStatus->getClearAt()->format('U')
|
||||
: null,
|
||||
];
|
||||
}
|
||||
} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
|
||||
$group = $this->groupManager->get($share->getSharedWith());
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
|
||||
} elseif ($share->getShareType() === IShare::TYPE_LINK) {
|
||||
|
||||
// "share_with" and "share_with_displayname" for passwords of link
|
||||
// shares was deprecated in Nextcloud 15, use "password" instead.
|
||||
$result['share_with'] = $share->getPassword();
|
||||
$result['share_with_displayname'] = '(' . $this->l->t('Shared link') . ')';
|
||||
|
||||
$result['password'] = $share->getPassword();
|
||||
|
||||
$result['send_password_by_talk'] = $share->getSendPasswordByTalk();
|
||||
|
||||
$result['token'] = $share->getToken();
|
||||
$result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
|
||||
} elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = $this->getCachedFederatedDisplayName($share->getSharedWith());
|
||||
$result['token'] = $share->getToken();
|
||||
} elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
|
||||
$result['token'] = $share->getToken();
|
||||
} elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['password'] = $share->getPassword();
|
||||
$result['password_expiration_time'] = $share->getPasswordExpirationTime() !== null ? $share->getPasswordExpirationTime()->format(\DateTime::ATOM) : null;
|
||||
$result['send_password_by_talk'] = $share->getSendPasswordByTalk();
|
||||
$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
|
||||
$result['token'] = $share->getToken();
|
||||
} elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
|
||||
// getSharedWith() returns either "name (type, owner)" or
|
||||
// "name (type, owner) [id]", depending on the Teams app version.
|
||||
$hasCircleId = (substr($share->getSharedWith(), -1) === ']');
|
||||
|
||||
$result['share_with_displayname'] = $share->getSharedWithDisplayName();
|
||||
if (empty($result['share_with_displayname'])) {
|
||||
$displayNameLength = ($hasCircleId ? strrpos($share->getSharedWith(), ' ') : strlen($share->getSharedWith()));
|
||||
$result['share_with_displayname'] = substr($share->getSharedWith(), 0, $displayNameLength);
|
||||
}
|
||||
|
||||
$result['share_with_avatar'] = $share->getSharedWithAvatar();
|
||||
|
||||
$shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
|
||||
$shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
|
||||
if ($shareWithLength === false) {
|
||||
$result['share_with'] = substr($share->getSharedWith(), $shareWithStart);
|
||||
} else {
|
||||
$result['share_with'] = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
|
||||
}
|
||||
} elseif ($share->getShareType() === IShare::TYPE_ROOM) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = '';
|
||||
|
||||
try {
|
||||
/** @var array{share_with_displayname: string, share_with_link: string, share_with?: string, token?: string} $roomShare */
|
||||
$roomShare = $this->getRoomShareHelper()->formatShare($share);
|
||||
$result = array_merge($result, $roomShare);
|
||||
} catch (QueryException $e) {
|
||||
}
|
||||
} elseif ($share->getShareType() === IShare::TYPE_DECK) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = '';
|
||||
|
||||
try {
|
||||
/** @var array{share_with: string, share_with_displayname: string, share_with_link: string} $deckShare */
|
||||
$deckShare = $this->getDeckShareHelper()->formatShare($share);
|
||||
$result = array_merge($result, $deckShare);
|
||||
} catch (QueryException $e) {
|
||||
}
|
||||
} elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = '';
|
||||
|
||||
try {
|
||||
/** @var array{share_with: string, share_with_displayname: string, token: string} $scienceMeshShare */
|
||||
$scienceMeshShare = $this->getSciencemeshShareHelper()->formatShare($share);
|
||||
$result = array_merge($result, $scienceMeshShare);
|
||||
} catch (QueryException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$result['mail_send'] = $share->getMailSend() ? 1 : 0;
|
||||
$result['hide_download'] = $share->getHideDownload() ? 1 : 0;
|
||||
|
||||
$result['attributes'] = null;
|
||||
if ($attributes = $share->getAttributes()) {
|
||||
$result['attributes'] = (string)\json_encode($attributes->toArray());
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if one of the users address books knows the exact property, if
|
||||
* not we return the full name.
|
||||
*
|
||||
* @param string $query
|
||||
* @param string $property
|
||||
* @return string
|
||||
*/
|
||||
private function getDisplayNameFromAddressBook(string $query, string $property): string {
|
||||
// FIXME: If we inject the contacts manager it gets initialized before any address books are registered
|
||||
try {
|
||||
$result = \OC::$server->getContactsManager()->search($query, [$property], [
|
||||
'limit' => 1,
|
||||
'enumeration' => false,
|
||||
'strict_search' => true,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error(
|
||||
$e->getMessage(),
|
||||
['exception' => $e]
|
||||
);
|
||||
return $query;
|
||||
}
|
||||
|
||||
foreach ($result as $r) {
|
||||
foreach ($r[$property] as $value) {
|
||||
if ($value === $query && $r['FN']) {
|
||||
return $r['FN'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $shares
|
||||
* @param array|null $updatedDisplayName
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function fixMissingDisplayName(array $shares, ?array $updatedDisplayName = null): array {
|
||||
$userIds = $updated = [];
|
||||
foreach ($shares as $share) {
|
||||
// share is federated and share have no display name yet
|
||||
if ($share['share_type'] === IShare::TYPE_REMOTE
|
||||
&& ($share['share_with'] ?? '') !== ''
|
||||
&& ($share['share_with_displayname'] ?? '') === '') {
|
||||
$userIds[] = $userId = $share['share_with'];
|
||||
|
||||
if ($updatedDisplayName !== null && array_key_exists($userId, $updatedDisplayName)) {
|
||||
$share['share_with_displayname'] = $updatedDisplayName[$userId];
|
||||
}
|
||||
}
|
||||
|
||||
// prepping userIds with displayName to be updated
|
||||
$updated[] = $share;
|
||||
}
|
||||
|
||||
// if $updatedDisplayName is not null, it means we should have already fixed displayNames of the shares
|
||||
if ($updatedDisplayName !== null) {
|
||||
return $updated;
|
||||
}
|
||||
|
||||
// get displayName for the generated list of userId with no displayName
|
||||
$displayNames = $this->retrieveFederatedDisplayName($userIds);
|
||||
|
||||
// if no displayName are updated, we exit
|
||||
if (empty($displayNames)) {
|
||||
return $updated;
|
||||
}
|
||||
|
||||
// let's fix missing display name and returns all shares
|
||||
return $this->fixMissingDisplayName($shares, $displayNames);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get displayName of a list of userIds from the lookup-server; through the globalsiteselector app.
|
||||
* returns an array with userIds as keys and displayName as values.
|
||||
*
|
||||
* @param array $userIds
|
||||
* @param bool $cacheOnly - do not reach LUS, get data from cache.
|
||||
*
|
||||
* @return array
|
||||
* @throws ContainerExceptionInterface
|
||||
*/
|
||||
private function retrieveFederatedDisplayName(array $userIds, bool $cacheOnly = false): array {
|
||||
// check if gss is enabled and available
|
||||
if (count($userIds) === 0
|
||||
|| !$this->appManager->isInstalled('globalsiteselector')
|
||||
|| !class_exists('\OCA\GlobalSiteSelector\Service\SlaveService')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
$slaveService = Server::get(SlaveService::class);
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->error(
|
||||
$e->getMessage(),
|
||||
['exception' => $e]
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
return $slaveService->getUsersDisplayName($userIds, $cacheOnly);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* retrieve displayName from cache if available (should be used on federated shares)
|
||||
* if not available in cache/lus, try for get from address-book, else returns empty string.
|
||||
*
|
||||
* @param string $userId
|
||||
* @param bool $cacheOnly if true will not reach the lus but will only get data from cache
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getCachedFederatedDisplayName(string $userId, bool $cacheOnly = true): string {
|
||||
$details = $this->retrieveFederatedDisplayName([$userId], $cacheOnly);
|
||||
if (array_key_exists($userId, $details)) {
|
||||
return $details[$userId];
|
||||
}
|
||||
|
||||
$displayName = $this->getDisplayNameFromAddressBook($userId, 'CLOUD');
|
||||
return ($displayName === $userId) ? '' : $displayName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get a specific share by id
|
||||
*
|
||||
|
|
@ -1528,150 +1183,6 @@ class ShareAPIController extends OCSController {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the user have edit permission on the share
|
||||
*
|
||||
* @param \OCP\Share\IShare $share the share to check
|
||||
* @return boolean
|
||||
*/
|
||||
protected function canEditShare(IShare $share): bool {
|
||||
// A file with permissions 0 can't be accessed by us. So Don't show it
|
||||
if ($share->getPermissions() === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The owner of the file and the creator of the share
|
||||
// can always edit the share
|
||||
if ($share->getShareOwner() === $this->currentUser ||
|
||||
$share->getSharedBy() === $this->currentUser
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//! we do NOT support some kind of `admin` in groups.
|
||||
//! You cannot edit shares shared to a group you're
|
||||
//! a member of if you're not the share owner or the file owner!
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the user have delete permission on the share
|
||||
*
|
||||
* @param \OCP\Share\IShare $share the share to check
|
||||
* @return boolean
|
||||
*/
|
||||
protected function canDeleteShare(IShare $share): bool {
|
||||
// A file with permissions 0 can't be accessed by us. So Don't show it
|
||||
if ($share->getPermissions() === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the user is the recipient, i can unshare
|
||||
// the share with self
|
||||
if ($share->getShareType() === IShare::TYPE_USER &&
|
||||
$share->getSharedWith() === $this->currentUser
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The owner of the file and the creator of the share
|
||||
// can always delete the share
|
||||
if ($share->getShareOwner() === $this->currentUser ||
|
||||
$share->getSharedBy() === $this->currentUser
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the user have delete permission on the share
|
||||
* This differs from the canDeleteShare function as it only
|
||||
* remove the share for the current user. It does NOT
|
||||
* completely delete the share but only the mount point.
|
||||
* It can then be restored from the deleted shares section.
|
||||
*
|
||||
* @param \OCP\Share\IShare $share the share to check
|
||||
* @return boolean
|
||||
*
|
||||
* @suppress PhanUndeclaredClassMethod
|
||||
*/
|
||||
protected function canDeleteShareFromSelf(IShare $share): bool {
|
||||
if ($share->getShareType() !== IShare::TYPE_GROUP &&
|
||||
$share->getShareType() !== IShare::TYPE_ROOM &&
|
||||
$share->getShareType() !== IShare::TYPE_DECK &&
|
||||
$share->getShareType() !== IShare::TYPE_SCIENCEMESH
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($share->getShareOwner() === $this->currentUser ||
|
||||
$share->getSharedBy() === $this->currentUser
|
||||
) {
|
||||
// Delete the whole share, not just for self
|
||||
return false;
|
||||
}
|
||||
|
||||
// If in the recipient group, you can delete the share from self
|
||||
if ($share->getShareType() === IShare::TYPE_GROUP) {
|
||||
$sharedWith = $this->groupManager->get($share->getSharedWith());
|
||||
$user = $this->userManager->get($this->currentUser);
|
||||
if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($share->getShareType() === IShare::TYPE_ROOM) {
|
||||
try {
|
||||
return $this->getRoomShareHelper()->canAccessShare($share, $this->currentUser);
|
||||
} catch (QueryException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($share->getShareType() === IShare::TYPE_DECK) {
|
||||
try {
|
||||
return $this->getDeckShareHelper()->canAccessShare($share, $this->currentUser);
|
||||
} catch (QueryException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
|
||||
try {
|
||||
return $this->getSciencemeshShareHelper()->canAccessShare($share, $this->currentUser);
|
||||
} catch (QueryException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the passed date is valid ISO 8601
|
||||
* So YYYY-MM-DD
|
||||
* If not throw an exception
|
||||
*
|
||||
* @param string $expireDate
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return \DateTime
|
||||
*/
|
||||
private function parseDate(string $expireDate): \DateTime {
|
||||
try {
|
||||
$date = new \DateTime(trim($expireDate, '"'), $this->dateTimeZone->getTimeZone());
|
||||
// Make sure it expires at midnight in owner timezone
|
||||
$date->setTime(0, 0, 0);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($this->l->t('Invalid date. Format must be YYYY-MM-DD'));
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since we have multiple providers but the OCS Share API v1 does
|
||||
* not support this we need to check all backends.
|
||||
|
|
@ -1764,57 +1275,6 @@ class ShareAPIController extends OCSController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the helper of ShareAPIController for room shares.
|
||||
*
|
||||
* If the Talk application is not enabled or the helper is not available
|
||||
* a QueryException is thrown instead.
|
||||
*
|
||||
* @return \OCA\Talk\Share\Helper\ShareAPIController
|
||||
* @throws QueryException
|
||||
*/
|
||||
private function getRoomShareHelper() {
|
||||
if (!$this->appManager->isEnabledForUser('spreed')) {
|
||||
throw new QueryException();
|
||||
}
|
||||
|
||||
return $this->serverContainer->get('\OCA\Talk\Share\Helper\ShareAPIController');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the helper of ShareAPIHelper for deck shares.
|
||||
*
|
||||
* If the Deck application is not enabled or the helper is not available
|
||||
* a QueryException is thrown instead.
|
||||
*
|
||||
* @return \OCA\Deck\Sharing\ShareAPIHelper
|
||||
* @throws QueryException
|
||||
*/
|
||||
private function getDeckShareHelper() {
|
||||
if (!$this->appManager->isEnabledForUser('deck')) {
|
||||
throw new QueryException();
|
||||
}
|
||||
|
||||
return $this->serverContainer->get('\OCA\Deck\Sharing\ShareAPIHelper');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the helper of ShareAPIHelper for sciencemesh shares.
|
||||
*
|
||||
* If the sciencemesh application is not enabled or the helper is not available
|
||||
* a QueryException is thrown instead.
|
||||
*
|
||||
* @return \OCA\Deck\Sharing\ShareAPIHelper
|
||||
* @throws QueryException
|
||||
*/
|
||||
private function getSciencemeshShareHelper() {
|
||||
if (!$this->appManager->isEnabledForUser('sciencemesh')) {
|
||||
throw new QueryException();
|
||||
}
|
||||
|
||||
return $this->serverContainer->get('\OCA\ScienceMesh\Sharing\ShareAPIHelper');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $viewer
|
||||
* @param Node $node
|
||||
|
|
|
|||
628
apps/files_sharing/lib/Controller/ShareApiControllerFactory.php
Normal file
628
apps/files_sharing/lib/Controller/ShareApiControllerFactory.php
Normal file
|
|
@ -0,0 +1,628 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCA\Files_Sharing\Controller;
|
||||
|
||||
use OCA\Files_Sharing\AppInfo\Application;
|
||||
use OCA\Files_Sharing\ResponseDefinitions;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\OCSController;
|
||||
use OCP\AppFramework\QueryException;
|
||||
use OCP\Constants;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\Node;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\IDateTimeZone;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\IPreview;
|
||||
use OCP\IRequest;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Server;
|
||||
use OCP\Share\IManager as ShareManager;
|
||||
use OCP\Share\IShare;
|
||||
use OCP\UserStatus\IManager as UserStatusManager;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @psalm-import-type Files_SharingShare from ResponseDefinitions
|
||||
*/
|
||||
abstract class ShareApiControllerFactory extends OCSController {
|
||||
|
||||
protected ?string $currentUser;
|
||||
public bool $isDeletedShareController = false;
|
||||
|
||||
public function __construct(
|
||||
IRequest $request,
|
||||
protected ShareManager $shareManager,
|
||||
protected ?string $userId,
|
||||
protected IUserManager $userManager,
|
||||
protected IGroupManager $groupManager,
|
||||
protected IRootFolder $rootFolder,
|
||||
protected IAppManager $appManager,
|
||||
protected ContainerInterface $serverContainer,
|
||||
protected UserStatusManager $userStatusManager,
|
||||
protected IPreview $previewManager,
|
||||
protected IDateTimeZone $dateTimeZone,
|
||||
protected IURLGenerator $urlGenerator,
|
||||
protected IL10N $l,
|
||||
protected LoggerInterface $logger,
|
||||
) {
|
||||
parent::__construct(Application::APP_ID, $request);
|
||||
$this->currentUser = $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an IShare to an array for OCS output
|
||||
*
|
||||
* @param \OCP\Share\IShare $share
|
||||
* @param Node|null $recipientNode
|
||||
* @return Files_SharingShare
|
||||
* @throws NotFoundException In case the node can't be resolved.
|
||||
*
|
||||
* @suppress PhanUndeclaredClassMethod
|
||||
*/
|
||||
public function formatShare(IShare $share, ?Node $recipientNode = null): array {
|
||||
$sharedBy = $this->userManager->get($share->getSharedBy());
|
||||
$shareOwner = $this->userManager->get($share->getShareOwner());
|
||||
|
||||
$isOwnShare = false;
|
||||
if ($shareOwner !== null) {
|
||||
$isOwnShare = $shareOwner->getUID() === $this->currentUser;
|
||||
}
|
||||
|
||||
$result = [
|
||||
'id' => $share->getId(),
|
||||
'share_type' => $share->getShareType(),
|
||||
'uid_owner' => $share->getSharedBy(),
|
||||
'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(),
|
||||
// recipient permissions
|
||||
'permissions' => $share->getPermissions(),
|
||||
// current user permissions on this share
|
||||
'can_edit' => $this->canEditShare($share),
|
||||
'can_delete' => $this->canDeleteShare($share),
|
||||
'stime' => $share->getShareTime()->getTimestamp(),
|
||||
'parent' => null,
|
||||
'expiration' => null,
|
||||
'token' => null,
|
||||
'uid_file_owner' => $share->getShareOwner(),
|
||||
'note' => $share->getNote(),
|
||||
'label' => $share->getLabel(),
|
||||
'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(),
|
||||
];
|
||||
|
||||
$userFolder = $this->rootFolder->getUserFolder($this->isDeletedShareController ? $share->getSharedBy() : $this->currentUser);
|
||||
if ($recipientNode) {
|
||||
$node = $recipientNode;
|
||||
} else {
|
||||
$node = $userFolder->getFirstNodeById($share->getNodeId());
|
||||
if (!$node) {
|
||||
// fallback to guessing the path
|
||||
$node = $userFolder->get($share->getTarget());
|
||||
if ($node === null || $share->getTarget() === '') {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result['path'] = $userFolder->getRelativePath($node->getPath());
|
||||
if ($node instanceof Folder) {
|
||||
$result['item_type'] = 'folder';
|
||||
} else {
|
||||
$result['item_type'] = 'file';
|
||||
}
|
||||
|
||||
// Get the original node permission if the share owner is the current user
|
||||
if ($isOwnShare) {
|
||||
$result['item_permissions'] = $node->getPermissions();
|
||||
}
|
||||
|
||||
// If we're on the recipient side, the node permissions
|
||||
// are bound to the share permissions. So we need to
|
||||
// adjust the permissions to the share permissions if necessary.
|
||||
if (!$isOwnShare) {
|
||||
$result['item_permissions'] = $share->getPermissions();
|
||||
|
||||
// For some reason, single files share are forbidden to have the delete permission
|
||||
// since we have custom methods to check those, let's adjust straight away.
|
||||
// DAV permissions does not have that issue though.
|
||||
if ($this->canDeleteShare($share) || $this->canDeleteShareFromSelf($share)) {
|
||||
$result['item_permissions'] |= Constants::PERMISSION_DELETE;
|
||||
}
|
||||
if ($this->canEditShare($share)) {
|
||||
$result['item_permissions'] |= Constants::PERMISSION_UPDATE;
|
||||
}
|
||||
}
|
||||
|
||||
// See MOUNT_ROOT_PROPERTYNAME dav property
|
||||
$result['is-mount-root'] = $node->getInternalPath() === '';
|
||||
$result['mount-type'] = $node->getMountPoint()->getMountType();
|
||||
|
||||
$result['mimetype'] = $node->getMimetype();
|
||||
$result['has_preview'] = $this->previewManager->isAvailable($node);
|
||||
$result['storage_id'] = $node->getStorage()->getId();
|
||||
$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
|
||||
$result['item_source'] = $node->getId();
|
||||
$result['file_source'] = $node->getId();
|
||||
$result['file_parent'] = $node->getParent()->getId();
|
||||
$result['file_target'] = $share->getTarget();
|
||||
$result['item_size'] = $node->getSize();
|
||||
$result['item_mtime'] = $node->getMTime();
|
||||
|
||||
$expiration = $share->getExpirationDate();
|
||||
if ($expiration !== null) {
|
||||
$expiration->setTimezone($this->dateTimeZone->getTimeZone());
|
||||
$result['expiration'] = $expiration->format('Y-m-d 00:00:00');
|
||||
}
|
||||
|
||||
if ($share->getShareType() === IShare::TYPE_USER) {
|
||||
$sharedWith = $this->userManager->get($share->getSharedWith());
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith();
|
||||
$result['share_with_displayname_unique'] = $sharedWith !== null ? (
|
||||
!empty($sharedWith->getSystemEMailAddress()) ? $sharedWith->getSystemEMailAddress() : $sharedWith->getUID()
|
||||
) : $share->getSharedWith();
|
||||
|
||||
$userStatuses = $this->userStatusManager->getUserStatuses([$share->getSharedWith()]);
|
||||
$userStatus = array_shift($userStatuses);
|
||||
if ($userStatus) {
|
||||
$result['status'] = [
|
||||
'status' => $userStatus->getStatus(),
|
||||
'message' => $userStatus->getMessage(),
|
||||
'icon' => $userStatus->getIcon(),
|
||||
'clearAt' => $userStatus->getClearAt()
|
||||
? (int)$userStatus->getClearAt()->format('U')
|
||||
: null,
|
||||
];
|
||||
}
|
||||
} elseif ($share->getShareType() === IShare::TYPE_GROUP) {
|
||||
$group = $this->groupManager->get($share->getSharedWith());
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith();
|
||||
} elseif ($share->getShareType() === IShare::TYPE_LINK) {
|
||||
|
||||
// "share_with" and "share_with_displayname" for passwords of link
|
||||
// shares was deprecated in Nextcloud 15, use "password" instead.
|
||||
$result['share_with'] = $share->getPassword();
|
||||
$result['share_with_displayname'] = '(' . $this->l->t('Shared link') . ')';
|
||||
|
||||
$result['password'] = $share->getPassword();
|
||||
|
||||
$result['send_password_by_talk'] = $share->getSendPasswordByTalk();
|
||||
|
||||
$result['token'] = $share->getToken();
|
||||
$result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]);
|
||||
} elseif ($share->getShareType() === IShare::TYPE_REMOTE) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = $this->getCachedFederatedDisplayName($share->getSharedWith());
|
||||
$result['token'] = $share->getToken();
|
||||
} elseif ($share->getShareType() === IShare::TYPE_REMOTE_GROUP) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD');
|
||||
$result['token'] = $share->getToken();
|
||||
} elseif ($share->getShareType() === IShare::TYPE_EMAIL) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['password'] = $share->getPassword();
|
||||
$result['password_expiration_time'] = $share->getPasswordExpirationTime() !== null ? $share->getPasswordExpirationTime()->format(\DateTime::ATOM) : null;
|
||||
$result['send_password_by_talk'] = $share->getSendPasswordByTalk();
|
||||
$result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL');
|
||||
$result['token'] = $share->getToken();
|
||||
} elseif ($share->getShareType() === IShare::TYPE_CIRCLE) {
|
||||
// getSharedWith() returns either "name (type, owner)" or
|
||||
// "name (type, owner) [id]", depending on the Teams app version.
|
||||
$hasCircleId = (substr($share->getSharedWith(), -1) === ']');
|
||||
|
||||
$result['share_with_displayname'] = $share->getSharedWithDisplayName();
|
||||
if (empty($result['share_with_displayname'])) {
|
||||
$displayNameLength = ($hasCircleId ? strrpos($share->getSharedWith(), ' ') : strlen($share->getSharedWith()));
|
||||
$result['share_with_displayname'] = substr($share->getSharedWith(), 0, $displayNameLength);
|
||||
}
|
||||
|
||||
$result['share_with_avatar'] = $share->getSharedWithAvatar();
|
||||
|
||||
$shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
|
||||
$shareWithLength = ($hasCircleId ? -1 : strpos($share->getSharedWith(), ' '));
|
||||
if ($shareWithLength === false) {
|
||||
$result['share_with'] = substr($share->getSharedWith(), $shareWithStart);
|
||||
} else {
|
||||
$result['share_with'] = substr($share->getSharedWith(), $shareWithStart, $shareWithLength);
|
||||
}
|
||||
} elseif ($share->getShareType() === IShare::TYPE_ROOM) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = '';
|
||||
|
||||
try {
|
||||
$roomShareHelper = $this->isDeletedShareController
|
||||
? $this->getDeletedRoomShareHelper()
|
||||
: $this->getRoomShareHelper();
|
||||
|
||||
/** @var array{share_with_displayname: string, share_with_link: string, share_with?: string, token?: string} $roomShare */
|
||||
$roomShare = $roomShareHelper->formatShare($share);
|
||||
$result = array_merge($result, $roomShare);
|
||||
} catch (QueryException $e) {
|
||||
}
|
||||
} elseif ($share->getShareType() === IShare::TYPE_DECK) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = '';
|
||||
|
||||
try {
|
||||
/** @var array{share_with: string, share_with_displayname: string, share_with_link: string} $deckShare */
|
||||
$deckShare = $this->getDeckShareHelper()->formatShare($share);
|
||||
$result = array_merge($result, $deckShare);
|
||||
} catch (QueryException $e) {
|
||||
}
|
||||
} elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
|
||||
$result['share_with'] = $share->getSharedWith();
|
||||
$result['share_with_displayname'] = '';
|
||||
|
||||
try {
|
||||
/** @var array{share_with: string, share_with_displayname: string, token: string} $scienceMeshShare */
|
||||
$scienceMeshShare = $this->getSciencemeshShareHelper()->formatShare($share);
|
||||
$result = array_merge($result, $scienceMeshShare);
|
||||
} catch (QueryException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$result['mail_send'] = $share->getMailSend() ? 1 : 0;
|
||||
$result['hide_download'] = $share->getHideDownload() ? 1 : 0;
|
||||
|
||||
$result['attributes'] = null;
|
||||
if ($attributes = $share->getAttributes()) {
|
||||
$result['attributes'] = (string)\json_encode($attributes->toArray());
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the helper of ShareAPIController for room shares.
|
||||
*
|
||||
* If the Talk application is not enabled or the helper is not available
|
||||
* a QueryException is thrown instead.
|
||||
*
|
||||
* @return \OCA\Talk\Share\Helper\ShareAPIController
|
||||
* @throws QueryException
|
||||
*/
|
||||
public function getRoomShareHelper() {
|
||||
if (!$this->appManager->isEnabledForUser('spreed')) {
|
||||
throw new QueryException();
|
||||
}
|
||||
|
||||
return $this->serverContainer->get('\OCA\Talk\Share\Helper\ShareAPIController');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the helper of DeletedShareAPIController for room shares.
|
||||
*
|
||||
* If the Talk application is not enabled or the helper is not available
|
||||
* a QueryException is thrown instead.
|
||||
*
|
||||
* @return \OCA\Talk\Share\Helper\DeletedShareAPIController
|
||||
* @throws QueryException
|
||||
*/
|
||||
public function getDeletedRoomShareHelper() {
|
||||
if (!$this->appManager->isEnabledForUser('spreed')) {
|
||||
throw new QueryException();
|
||||
}
|
||||
|
||||
return $this->serverContainer->get('\OCA\Talk\Share\Helper\DeletedShareAPIController');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the helper of DeletedShareAPIHelper for deck shares.
|
||||
*
|
||||
* If the Deck application is not enabled or the helper is not available
|
||||
* a QueryException is thrown instead.
|
||||
*
|
||||
* @return \OCA\Deck\Sharing\ShareAPIHelper
|
||||
* @throws QueryException
|
||||
*/
|
||||
public function getDeckShareHelper() {
|
||||
if (!$this->appManager->isEnabledForUser('deck')) {
|
||||
throw new QueryException();
|
||||
}
|
||||
|
||||
return $this->serverContainer->get('\OCA\Deck\Sharing\ShareAPIHelper');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the helper of DeletedShareAPIHelper for sciencemesh shares.
|
||||
*
|
||||
* If the sciencemesh application is not enabled or the helper is not available
|
||||
* a QueryException is thrown instead.
|
||||
*
|
||||
* @return \OCA\Deck\Sharing\ShareAPIHelper
|
||||
* @throws QueryException
|
||||
*/
|
||||
public function getSciencemeshShareHelper() {
|
||||
if (!$this->appManager->isEnabledForUser('sciencemesh')) {
|
||||
throw new QueryException();
|
||||
}
|
||||
|
||||
return $this->serverContainer->get('\OCA\ScienceMesh\Sharing\ShareAPIHelper');
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the user have edit permission on the share
|
||||
*
|
||||
* @param \OCP\Share\IShare $share the share to check
|
||||
* @return boolean
|
||||
*/
|
||||
public function canEditShare(IShare $share): bool {
|
||||
// A file with permissions 0 can't be accessed by us. So Don't show it
|
||||
if ($share->getPermissions() === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The owner of the file and the creator of the share
|
||||
// can always edit the share
|
||||
if ($share->getShareOwner() === $this->currentUser ||
|
||||
$share->getSharedBy() === $this->currentUser
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//! we do NOT support some kind of `admin` in groups.
|
||||
//! You cannot edit shares shared to a group you're
|
||||
//! a member of if you're not the share owner or the file owner!
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the user have delete permission on the share
|
||||
*
|
||||
* @param \OCP\Share\IShare $share the share to check
|
||||
* @return boolean
|
||||
*/
|
||||
public function canDeleteShare(IShare $share): bool {
|
||||
// A file with permissions 0 can't be accessed by us. So Don't show it
|
||||
if ($share->getPermissions() === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the user is the recipient, i can unshare
|
||||
// the share with self
|
||||
if ($share->getShareType() === IShare::TYPE_USER &&
|
||||
$share->getSharedWith() === $this->currentUser
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The owner of the file and the creator of the share
|
||||
// can always delete the share
|
||||
if ($share->getShareOwner() === $this->currentUser ||
|
||||
$share->getSharedBy() === $this->currentUser
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the user have delete permission on the share
|
||||
* This differs from the canDeleteShare function as it only
|
||||
* remove the share for the current user. It does NOT
|
||||
* completely delete the share but only the mount point.
|
||||
* It can then be restored from the deleted shares section.
|
||||
*
|
||||
* @param \OCP\Share\IShare $share the share to check
|
||||
* @return boolean
|
||||
*
|
||||
* @suppress PhanUndeclaredClassMethod
|
||||
*/
|
||||
public function canDeleteShareFromSelf(IShare $share): bool {
|
||||
if ($share->getShareType() !== IShare::TYPE_GROUP &&
|
||||
$share->getShareType() !== IShare::TYPE_ROOM &&
|
||||
$share->getShareType() !== IShare::TYPE_DECK &&
|
||||
$share->getShareType() !== IShare::TYPE_SCIENCEMESH
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($share->getShareOwner() === $this->currentUser ||
|
||||
$share->getSharedBy() === $this->currentUser
|
||||
) {
|
||||
// Delete the whole share, not just for self
|
||||
return false;
|
||||
}
|
||||
|
||||
// If in the recipient group, you can delete the share from self
|
||||
if ($share->getShareType() === IShare::TYPE_GROUP) {
|
||||
$sharedWith = $this->groupManager->get($share->getSharedWith());
|
||||
$user = $this->userManager->get($this->currentUser);
|
||||
if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($share->getShareType() === IShare::TYPE_ROOM) {
|
||||
try {
|
||||
return $this->getRoomShareHelper()->canAccessShare($share, $this->currentUser);
|
||||
} catch (QueryException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($share->getShareType() === IShare::TYPE_DECK) {
|
||||
try {
|
||||
return $this->getDeckShareHelper()->canAccessShare($share, $this->currentUser);
|
||||
} catch (QueryException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
|
||||
try {
|
||||
return $this->getSciencemeshShareHelper()->canAccessShare($share, $this->currentUser);
|
||||
} catch (QueryException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the passed date is valid ISO 8601
|
||||
* So YYYY-MM-DD
|
||||
* If not throw an exception
|
||||
*
|
||||
* @param string $expireDate
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function parseDate(string $expireDate): \DateTime {
|
||||
try {
|
||||
$date = new \DateTime(trim($expireDate, '"'), $this->dateTimeZone->getTimeZone());
|
||||
// Make sure it expires at midnight in owner timezone
|
||||
$date->setTime(0, 0, 0);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($this->l->t('Invalid date. Format must be YYYY-MM-DD'));
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve displayName from cache if available (should be used on federated shares)
|
||||
* if not available in cache/lus, try for get from address-book, else returns empty string.
|
||||
*
|
||||
* @param string $userId
|
||||
* @param bool $cacheOnly if true will not reach the lus but will only get data from cache
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCachedFederatedDisplayName(string $userId, bool $cacheOnly = true): string {
|
||||
$details = $this->retrieveFederatedDisplayName([$userId], $cacheOnly);
|
||||
if (array_key_exists($userId, $details)) {
|
||||
return $details[$userId];
|
||||
}
|
||||
|
||||
$displayName = $this->getDisplayNameFromAddressBook($userId, 'CLOUD');
|
||||
return ($displayName === $userId) ? '' : $displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if one of the users address books knows the exact property, if
|
||||
* not we return the full name.
|
||||
*
|
||||
* @param string $query
|
||||
* @param string $property
|
||||
* @return string
|
||||
*/
|
||||
public function getDisplayNameFromAddressBook(string $query, string $property): string {
|
||||
// FIXME: If we inject the contacts manager it gets initialized before any address books are registered
|
||||
try {
|
||||
$result = \OC::$server->getContactsManager()->search($query, [$property], [
|
||||
'limit' => 1,
|
||||
'enumeration' => false,
|
||||
'strict_search' => true,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error(
|
||||
$e->getMessage(),
|
||||
['exception' => $e]
|
||||
);
|
||||
return $query;
|
||||
}
|
||||
|
||||
foreach ($result as $r) {
|
||||
foreach ($r[$property] as $value) {
|
||||
if ($value === $query && $r['FN']) {
|
||||
return $r['FN'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $shares
|
||||
* @param array|null $updatedDisplayName
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fixMissingDisplayName(array $shares, ?array $updatedDisplayName = null): array {
|
||||
$userIds = $updated = [];
|
||||
foreach ($shares as $share) {
|
||||
// share is federated and share have no display name yet
|
||||
if ($share['share_type'] === IShare::TYPE_REMOTE
|
||||
&& ($share['share_with'] ?? '') !== ''
|
||||
&& ($share['share_with_displayname'] ?? '') === '') {
|
||||
$userIds[] = $userId = $share['share_with'];
|
||||
|
||||
if ($updatedDisplayName !== null && array_key_exists($userId, $updatedDisplayName)) {
|
||||
$share['share_with_displayname'] = $updatedDisplayName[$userId];
|
||||
}
|
||||
}
|
||||
|
||||
// prepping userIds with displayName to be updated
|
||||
$updated[] = $share;
|
||||
}
|
||||
|
||||
// if $updatedDisplayName is not null, it means we should have already fixed displayNames of the shares
|
||||
if ($updatedDisplayName !== null) {
|
||||
return $updated;
|
||||
}
|
||||
|
||||
// get displayName for the generated list of userId with no displayName
|
||||
$displayNames = $this->retrieveFederatedDisplayName($userIds);
|
||||
|
||||
// if no displayName are updated, we exit
|
||||
if (empty($displayNames)) {
|
||||
return $updated;
|
||||
}
|
||||
|
||||
// let's fix missing display name and returns all shares
|
||||
return $this->fixMissingDisplayName($shares, $displayNames);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get displayName of a list of userIds from the lookup-server; through the globalsiteselector app.
|
||||
* returns an array with userIds as keys and displayName as values.
|
||||
*
|
||||
* @param array $userIds
|
||||
* @param bool $cacheOnly - do not reach LUS, get data from cache.
|
||||
*
|
||||
* @return array
|
||||
* @throws ContainerExceptionInterface
|
||||
*/
|
||||
public function retrieveFederatedDisplayName(array $userIds, bool $cacheOnly = false): array {
|
||||
// check if gss is enabled and available
|
||||
if (count($userIds) === 0
|
||||
|| !$this->appManager->isInstalled('globalsiteselector')
|
||||
|| !$this->appManager->isEnabledForUser('globalsiteselector')
|
||||
|| !class_exists('\OCA\GlobalSiteSelector\Service\SlaveService')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
/** @var \OCA\GlobalSiteSelector\Service\SlaveService $slaveService */
|
||||
$slaveService = $this->serverContainer->get('\OCA\GlobalSiteSelector\Service\SlaveService');
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->error(
|
||||
$e->getMessage(),
|
||||
['exception' => $e]
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
return $slaveService->getUsersDisplayName($userIds, $cacheOnly);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,8 +24,9 @@ class ExpireSharesJob extends TimedJob {
|
|||
ITimeFactory $time,
|
||||
private IManager $shareManager,
|
||||
private IDBConnection $db,
|
||||
private IAppConfig $config,
|
||||
private LoggerInterface $logger) {
|
||||
private IAppConfig $appConfig,
|
||||
private LoggerInterface $logger,
|
||||
) {
|
||||
|
||||
parent::__construct($time);
|
||||
|
||||
|
|
@ -41,7 +42,7 @@ class ExpireSharesJob extends TimedJob {
|
|||
* @param array $argument unused argument
|
||||
*/
|
||||
public function run($argument) {
|
||||
if ($this->config->getValueString('core', 'shareapi_delete_on_expire', 'yes') === 'no') {
|
||||
if ($this->appConfig->getValueString('core', 'shareapi_delete_on_expire', 'yes') === 'no') {
|
||||
$this->logger->info('Share deletion on expiration is disabled');
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,29 +56,6 @@ namespace OCA\Files_Sharing;
|
|||
* url?: string,
|
||||
* }
|
||||
*
|
||||
* @psalm-type Files_SharingDeletedShare = array{
|
||||
* id: string,
|
||||
* share_type: int,
|
||||
* uid_owner: string,
|
||||
* displayname_owner: string,
|
||||
* permissions: int,
|
||||
* stime: int,
|
||||
* uid_file_owner: string,
|
||||
* displayname_file_owner: string,
|
||||
* path: string,
|
||||
* item_type: string,
|
||||
* mimetype: string,
|
||||
* storage: int,
|
||||
* item_source: int,
|
||||
* file_source: int,
|
||||
* file_parent: int,
|
||||
* file_target: int,
|
||||
* expiration: string|null,
|
||||
* share_with: string|null,
|
||||
* share_with_displayname: string|null,
|
||||
* share_with_link: string|null,
|
||||
* }
|
||||
*
|
||||
* @psalm-type Files_SharingRemoteShare = array{
|
||||
* accepted: bool,
|
||||
* file_id: int|null,
|
||||
|
|
|
|||
|
|
@ -244,105 +244,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"DeletedShare": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"share_type",
|
||||
"uid_owner",
|
||||
"displayname_owner",
|
||||
"permissions",
|
||||
"stime",
|
||||
"uid_file_owner",
|
||||
"displayname_file_owner",
|
||||
"path",
|
||||
"item_type",
|
||||
"mimetype",
|
||||
"storage",
|
||||
"item_source",
|
||||
"file_source",
|
||||
"file_parent",
|
||||
"file_target",
|
||||
"expiration",
|
||||
"share_with",
|
||||
"share_with_displayname",
|
||||
"share_with_link"
|
||||
],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"share_type": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"uid_owner": {
|
||||
"type": "string"
|
||||
},
|
||||
"displayname_owner": {
|
||||
"type": "string"
|
||||
},
|
||||
"permissions": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"stime": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"uid_file_owner": {
|
||||
"type": "string"
|
||||
},
|
||||
"displayname_file_owner": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"item_type": {
|
||||
"type": "string"
|
||||
},
|
||||
"mimetype": {
|
||||
"type": "string"
|
||||
},
|
||||
"storage": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"item_source": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"file_source": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"file_parent": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"file_target": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"expiration": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"share_with": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"share_with_displayname": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"share_with_link": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"Lookup": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
@ -2891,7 +2792,7 @@
|
|||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/DeletedShare"
|
||||
"$ref": "#/components/schemas/Share"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3000,6 +2901,70 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/apps/files_sharing/api/v1/expiredshares": {
|
||||
"get": {
|
||||
"operationId": "expired_shareapi-index",
|
||||
"summary": "Get a list of all expired shares",
|
||||
"tags": [
|
||||
"expired_shareapi"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Deleted shares returned",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Share"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/apps/files_sharing/api/v1/sharees": {
|
||||
"get": {
|
||||
"operationId": "shareesapi-search",
|
||||
|
|
|
|||
|
|
@ -34,12 +34,12 @@ describe('Sharing views definition', () => {
|
|||
const shareOverviewView = Navigation.views.find(view => view.id === 'shareoverview') as View
|
||||
const sharesChildViews = Navigation.views.filter(view => view.parent === 'shareoverview') as View[]
|
||||
|
||||
expect(Navigation.register).toHaveBeenCalledTimes(7)
|
||||
expect(Navigation.register).toHaveBeenCalledTimes(8)
|
||||
|
||||
// one main view and no children
|
||||
expect(Navigation.views.length).toBe(7)
|
||||
expect(Navigation.views.length).toBe(8)
|
||||
expect(shareOverviewView).toBeDefined()
|
||||
expect(sharesChildViews.length).toBe(6)
|
||||
expect(sharesChildViews.length).toBe(7)
|
||||
|
||||
expect(shareOverviewView?.id).toBe('shareoverview')
|
||||
expect(shareOverviewView?.name).toBe('Shares')
|
||||
|
|
@ -55,6 +55,7 @@ describe('Sharing views definition', () => {
|
|||
{ id: 'sharinglinks', name: 'Shared by link' },
|
||||
{ id: 'filerequest', name: 'File requests' },
|
||||
{ id: 'deletedshares', name: 'Deleted shares' },
|
||||
{ id: 'expiredshares', name: 'Expired shares', columns: 1 },
|
||||
{ id: 'pendingshares', name: 'Pending shares' },
|
||||
]
|
||||
|
||||
|
|
@ -67,7 +68,7 @@ describe('Sharing views definition', () => {
|
|||
expect(view?.emptyCaption).toBeDefined()
|
||||
expect(view?.icon).match(/<svg.+<\/svg>/)
|
||||
expect(view?.order).toBe(index + 1)
|
||||
expect(view?.columns).toStrictEqual([])
|
||||
expect(view?.columns).toHaveLength(dataProvider[index].columns || 0)
|
||||
expect(view?.getContents).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
|
@ -98,7 +99,7 @@ describe('Sharing views contents', () => {
|
|||
})
|
||||
|
||||
registerSharingViews()
|
||||
expect(Navigation.views.length).toBe(7)
|
||||
expect(Navigation.views.length).toBe(8)
|
||||
Navigation.views.forEach(async (view: View) => {
|
||||
const content = await view.getContents('/')
|
||||
expect(content.contents).toStrictEqual([])
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ describe('SharingService methods definitions', () => {
|
|||
})
|
||||
|
||||
test('Shared with you', async () => {
|
||||
await getContents(true, false, false, false, [])
|
||||
await getContents(true, false, false, false, false, [])
|
||||
|
||||
expect(axios.get).toHaveBeenCalledTimes(2)
|
||||
expect(axios.get).toHaveBeenNthCalledWith(1, 'http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares', {
|
||||
|
|
@ -69,7 +69,7 @@ describe('SharingService methods definitions', () => {
|
|||
})
|
||||
|
||||
test('Shared with others', async () => {
|
||||
await getContents(false, true, false, false, [])
|
||||
await getContents(false, true, false, false, false, [])
|
||||
|
||||
expect(axios.get).toHaveBeenCalledTimes(1)
|
||||
expect(axios.get).toHaveBeenCalledWith('http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares', {
|
||||
|
|
@ -84,7 +84,7 @@ describe('SharingService methods definitions', () => {
|
|||
})
|
||||
|
||||
test('Pending shares', async () => {
|
||||
await getContents(false, false, true, false, [])
|
||||
await getContents(false, false, true, false, false, [])
|
||||
|
||||
expect(axios.get).toHaveBeenCalledTimes(2)
|
||||
expect(axios.get).toHaveBeenNthCalledWith(1, 'http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares/pending', {
|
||||
|
|
@ -106,7 +106,7 @@ describe('SharingService methods definitions', () => {
|
|||
})
|
||||
|
||||
test('Deleted shares', async () => {
|
||||
await getContents(false, true, false, false, [])
|
||||
await getContents(false, true, false, false, false, [])
|
||||
|
||||
expect(axios.get).toHaveBeenCalledTimes(1)
|
||||
expect(axios.get).toHaveBeenCalledWith('http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/shares', {
|
||||
|
|
@ -120,9 +120,23 @@ describe('SharingService methods definitions', () => {
|
|||
})
|
||||
})
|
||||
|
||||
test('Expired shares', async () => {
|
||||
await getContents(false, false, false, false, true, [])
|
||||
|
||||
expect(axios.get).toHaveBeenCalledTimes(1)
|
||||
expect(axios.get).toHaveBeenCalledWith('http://nextcloud.local/ocs/v2.php/apps/files_sharing/api/v1/expiredshares', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: {
|
||||
include_tags: true,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('Unknown owner', async () => {
|
||||
vi.spyOn(auth, 'getCurrentUser').mockReturnValue(null)
|
||||
const results = await getContents(false, true, false, false, [])
|
||||
const results = await getContents(false, true, false, false, false, [])
|
||||
|
||||
expect(results.folder.owner).toEqual(null)
|
||||
})
|
||||
|
|
@ -170,7 +184,7 @@ describe('SharingService filtering', () => {
|
|||
})
|
||||
|
||||
test('Shared with others filtering', async () => {
|
||||
const shares = await getContents(false, true, false, false, [ShareType.User])
|
||||
const shares = await getContents(false, true, false, false, false, [ShareType.User])
|
||||
|
||||
expect(axios.get).toHaveBeenCalledTimes(1)
|
||||
expect(shares.contents).toHaveLength(1)
|
||||
|
|
@ -179,7 +193,7 @@ describe('SharingService filtering', () => {
|
|||
})
|
||||
|
||||
test('Shared with others filtering empty', async () => {
|
||||
const shares = await getContents(false, true, false, false, [ShareType.Link])
|
||||
const shares = await getContents(false, true, false, false, false, [ShareType.Link])
|
||||
|
||||
expect(axios.get).toHaveBeenCalledTimes(1)
|
||||
expect(shares.contents).toHaveLength(0)
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ class ApiTest extends TestCase {
|
|||
$dateTimeZone->method('getTimeZone')->willReturn(new \DateTimeZone(date_default_timezone_get()));
|
||||
|
||||
return new ShareAPIController(
|
||||
self::APP_NAME,
|
||||
$this->getMockBuilder(IRequest::class)->getMock(),
|
||||
$this->shareManager,
|
||||
\OC::$server->getGroupManager(),
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ class ShareAPIControllerTest extends TestCase {
|
|||
$this->mailer = $this->createMock(IMailer::class);
|
||||
|
||||
$this->ocs = new ShareAPIController(
|
||||
$this->appName,
|
||||
$this->request,
|
||||
$this->shareManager,
|
||||
$this->groupManager,
|
||||
|
|
@ -135,7 +134,6 @@ class ShareAPIControllerTest extends TestCase {
|
|||
private function mockFormatShare() {
|
||||
return $this->getMockBuilder(ShareAPIController::class)
|
||||
->setConstructorArgs([
|
||||
$this->appName,
|
||||
$this->request,
|
||||
$this->shareManager,
|
||||
$this->groupManager,
|
||||
|
|
@ -752,7 +750,6 @@ class ShareAPIControllerTest extends TestCase {
|
|||
/** @var ShareAPIController|\PHPUnit\Framework\MockObject\MockObject $ocs */
|
||||
$ocs = $this->getMockBuilder(ShareAPIController::class)
|
||||
->setConstructorArgs([
|
||||
$this->appName,
|
||||
$this->request,
|
||||
$this->shareManager,
|
||||
$this->groupManager,
|
||||
|
|
@ -1386,7 +1383,6 @@ class ShareAPIControllerTest extends TestCase {
|
|||
/** @var ShareAPIController $ocs */
|
||||
$ocs = $this->getMockBuilder(ShareAPIController::class)
|
||||
->setConstructorArgs([
|
||||
$this->appName,
|
||||
$this->request,
|
||||
$this->shareManager,
|
||||
$this->groupManager,
|
||||
|
|
@ -1729,7 +1725,6 @@ class ShareAPIControllerTest extends TestCase {
|
|||
/** @var ShareAPIController $ocs */
|
||||
$ocs = $this->getMockBuilder(ShareAPIController::class)
|
||||
->setConstructorArgs([
|
||||
$this->appName,
|
||||
$this->request,
|
||||
$this->shareManager,
|
||||
$this->groupManager,
|
||||
|
|
@ -1827,7 +1822,6 @@ class ShareAPIControllerTest extends TestCase {
|
|||
/** @var ShareAPIController|\PHPUnit\Framework\MockObject\MockObject $ocs */
|
||||
$ocs = $this->getMockBuilder(ShareAPIController::class)
|
||||
->setConstructorArgs([
|
||||
$this->appName,
|
||||
$this->request,
|
||||
$this->shareManager,
|
||||
$this->groupManager,
|
||||
|
|
@ -2249,7 +2243,6 @@ class ShareAPIControllerTest extends TestCase {
|
|||
/** @var ShareAPIController $ocs */
|
||||
$ocs = $this->getMockBuilder(ShareAPIController::class)
|
||||
->setConstructorArgs([
|
||||
$this->appName,
|
||||
$this->request,
|
||||
$this->shareManager,
|
||||
$this->groupManager,
|
||||
|
|
@ -2319,7 +2312,6 @@ class ShareAPIControllerTest extends TestCase {
|
|||
/** @var ShareAPIController $ocs */
|
||||
$ocs = $this->getMockBuilder(ShareAPIController::class)
|
||||
->setConstructorArgs([
|
||||
$this->appName,
|
||||
$this->request,
|
||||
$this->shareManager,
|
||||
$this->groupManager,
|
||||
|
|
@ -2562,7 +2554,6 @@ class ShareAPIControllerTest extends TestCase {
|
|||
/** @var ShareAPIController|\PHPUnit\Framework\MockObject\MockObject $ocs */
|
||||
$ocs = $this->getMockBuilder(ShareAPIController::class)
|
||||
->setConstructorArgs([
|
||||
$this->appName,
|
||||
$this->request,
|
||||
$this->shareManager,
|
||||
$this->groupManager,
|
||||
|
|
|
|||
|
|
@ -83,10 +83,10 @@ class Sharing implements IDelegatedSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Helper function to retrive boolean values from human readable strings ('yes' / 'no')
|
||||
* Helper function to retrieve boolean values from human readable strings ('yes' / 'no')
|
||||
*/
|
||||
private function getHumanBooleanConfig(string $app, string $key, bool $default = false): bool {
|
||||
return $this->config->getAppValue($app, $key, $default ? 'yes' : 'no') === 'yes';
|
||||
return $this->config->getAppValue($app, $key, $default ? 'yes' : 'no') !== 'no';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ class SharingTest extends TestCase {
|
|||
['core', 'shareapi_enforce_remote_expire_date', 'no', 'no'],
|
||||
['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
|
||||
['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
|
||||
['core', 'deleteOnExpire', 'yes', 'yes'],
|
||||
]);
|
||||
$this->shareManager->method('shareWithGroupMembersOnly')
|
||||
->willReturn(false);
|
||||
|
|
@ -139,6 +140,7 @@ class SharingTest extends TestCase {
|
|||
'onlyShareWithGroupMembersExcludeGroupList' => [],
|
||||
'enforceLinksPasswordExcludedGroups' => [],
|
||||
'enforceLinksPasswordExcludedGroupsEnabled' => false,
|
||||
'deleteOnExpire' => true
|
||||
]
|
||||
],
|
||||
);
|
||||
|
|
@ -232,6 +234,7 @@ class SharingTest extends TestCase {
|
|||
'onlyShareWithGroupMembersExcludeGroupList' => [],
|
||||
'enforceLinksPasswordExcludedGroups' => [],
|
||||
'enforceLinksPasswordExcludedGroupsEnabled' => false,
|
||||
'deleteOnExpire' => true,
|
||||
]
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -874,12 +874,6 @@
|
|||
<code><![CDATA[Circles]]></code>
|
||||
<code><![CDATA[Circles]]></code>
|
||||
</UndefinedClass>
|
||||
<UndefinedDocblockClass>
|
||||
<code><![CDATA[$this->getRoomShareHelper()]]></code>
|
||||
<code><![CDATA[$this->getRoomShareHelper()]]></code>
|
||||
<code><![CDATA[$this->getRoomShareHelper()]]></code>
|
||||
<code><![CDATA[\OCA\Talk\Share\Helper\ShareAPIController]]></code>
|
||||
</UndefinedDocblockClass>
|
||||
</file>
|
||||
<file src="apps/files_sharing/lib/External/Manager.php">
|
||||
<LessSpecificReturnStatement>
|
||||
|
|
|
|||
2
dist/4757-4757.js.map
vendored
2
dist/4757-4757.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/files_sharing-init.js
vendored
4
dist/files_sharing-init.js
vendored
File diff suppressed because one or more lines are too long
14
dist/files_sharing-init.js.license
vendored
14
dist/files_sharing-init.js.license
vendored
|
|
@ -29,6 +29,7 @@ SPDX-FileCopyrightText: John-David Dalton <john.david.dalton@gmail.com> (http://
|
|||
SPDX-FileCopyrightText: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
|
||||
SPDX-FileCopyrightText: Jeff Sagal <sagalbot@gmail.com>
|
||||
SPDX-FileCopyrightText: Jacob Clevenger<https://github.com/wheatjs>
|
||||
SPDX-FileCopyrightText: Iskren Ivov Chernev <iskren.chernev@gmail.com> (https://github.com/ichernev)
|
||||
SPDX-FileCopyrightText: Hypercontext
|
||||
SPDX-FileCopyrightText: Guillaume Chau <guillaume.b.chau@gmail.com>
|
||||
SPDX-FileCopyrightText: GitHub Inc.
|
||||
|
|
@ -37,6 +38,7 @@ SPDX-FileCopyrightText: Evan You
|
|||
SPDX-FileCopyrightText: Eugene Sharygin <eush77@gmail.com>
|
||||
SPDX-FileCopyrightText: Eric Norris (https://github.com/ericnorris)
|
||||
SPDX-FileCopyrightText: Dr.-Ing. Mario Heiderich, Cure53 <mario@cure53.de> (https://cure53.de/)
|
||||
SPDX-FileCopyrightText: Denis Pushkarev
|
||||
SPDX-FileCopyrightText: David Clark
|
||||
SPDX-FileCopyrightText: Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
SPDX-FileCopyrightText: Christoph Wurst
|
||||
|
|
@ -93,6 +95,12 @@ This file is generated from multiple sources. Included packages:
|
|||
- @nextcloud/logger
|
||||
- version: 3.0.2
|
||||
- license: GPL-3.0-or-later
|
||||
- @nextcloud/router
|
||||
- version: 2.2.1
|
||||
- license: GPL-3.0-or-later
|
||||
- @nextcloud/moment
|
||||
- version: 1.3.1
|
||||
- license: GPL-3.0-or-later
|
||||
- @nextcloud/paths
|
||||
- version: 2.2.1
|
||||
- license: GPL-3.0-or-later
|
||||
|
|
@ -129,6 +137,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- charenc
|
||||
- version: 0.0.2
|
||||
- license: BSD-3-Clause
|
||||
- core-js
|
||||
- version: 3.38.1
|
||||
- license: MIT
|
||||
- crypt
|
||||
- version: 0.0.2
|
||||
- license: BSD-3-Clause
|
||||
|
|
@ -171,6 +182,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- md5
|
||||
- version: 2.3.0
|
||||
- license: BSD-3-Clause
|
||||
- moment
|
||||
- version: 2.30.1
|
||||
- license: MIT
|
||||
- node-gettext
|
||||
- version: 3.0.0
|
||||
- license: MIT
|
||||
|
|
|
|||
2
dist/files_sharing-init.js.map
vendored
2
dist/files_sharing-init.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/settings-vue-settings-admin-sharing.js
vendored
4
dist/settings-vue-settings-admin-sharing.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1143,7 +1143,7 @@ class Manager implements IManager {
|
|||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0, $expired = false) {
|
||||
public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0, $allowExpired = false) {
|
||||
if ($path !== null &&
|
||||
!($path instanceof \OCP\Files\File) &&
|
||||
!($path instanceof \OCP\Files\Folder)) {
|
||||
|
|
@ -1158,18 +1158,17 @@ class Manager implements IManager {
|
|||
|
||||
$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
|
||||
|
||||
/*
|
||||
/**
|
||||
* Work around so we don't return expired shares but still follow
|
||||
* proper pagination.
|
||||
*/
|
||||
|
||||
$shares2 = [];
|
||||
|
||||
while (true) {
|
||||
$added = 0;
|
||||
foreach ($shares as $share) {
|
||||
try {
|
||||
$this->checkShare($share, $expired);
|
||||
$this->checkShare($share, $allowExpired);
|
||||
} catch (ShareNotFound $e) {
|
||||
// Ignore since this basically means the share is deleted
|
||||
continue;
|
||||
|
|
@ -1216,7 +1215,7 @@ class Manager implements IManager {
|
|||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0, $expired = false) {
|
||||
public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0, $allowExpired = false) {
|
||||
try {
|
||||
$provider = $this->factory->getProviderForType($shareType);
|
||||
} catch (ProviderException $e) {
|
||||
|
|
@ -1228,7 +1227,7 @@ class Manager implements IManager {
|
|||
// remove all shares which are already expired
|
||||
foreach ($shares as $key => $share) {
|
||||
try {
|
||||
$this->checkShare($share, $expired);
|
||||
$this->checkShare($share, $allowExpired);
|
||||
} catch (ShareNotFound $e) {
|
||||
unset($shares[$key]);
|
||||
}
|
||||
|
|
@ -1270,7 +1269,7 @@ class Manager implements IManager {
|
|||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getShareById($id, $recipient = null, $expired = false) {
|
||||
public function getShareById($id, $recipient = null, $allowExpired = false) {
|
||||
if ($id === null) {
|
||||
throw new ShareNotFound();
|
||||
}
|
||||
|
|
@ -1285,7 +1284,7 @@ class Manager implements IManager {
|
|||
|
||||
$share = $provider->getShareById($id, $recipient);
|
||||
|
||||
$this->checkShare($share, $expired);
|
||||
$this->checkShare($share, $allowExpired);
|
||||
|
||||
return $share;
|
||||
}
|
||||
|
|
@ -1311,7 +1310,7 @@ class Manager implements IManager {
|
|||
*
|
||||
* @throws ShareNotFound
|
||||
*/
|
||||
public function getShareByToken($token, $expired = false) {
|
||||
public function getShareByToken($token, $allowExpired = false) {
|
||||
// tokens cannot be valid local user names
|
||||
if ($this->userManager->userExists($token)) {
|
||||
throw new ShareNotFound();
|
||||
|
|
@ -1369,7 +1368,7 @@ class Manager implements IManager {
|
|||
throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
|
||||
}
|
||||
|
||||
$this->checkShare($share, $expired);
|
||||
$this->checkShare($share, $allowExpired);
|
||||
|
||||
/*
|
||||
* Reduce the permissions for link or email shares if public upload is not enabled
|
||||
|
|
@ -1385,6 +1384,8 @@ class Manager implements IManager {
|
|||
/**
|
||||
* Check expire date and disabled owner
|
||||
*
|
||||
* @param IShare $share
|
||||
* @param bool $allowExpired will throw if share is expired and $allowExpired is false
|
||||
* @throws ShareNotFound
|
||||
*/
|
||||
protected function checkShare(IShare $share, $allowExpired = false): void {
|
||||
|
|
|
|||
|
|
@ -127,10 +127,12 @@ interface IManager {
|
|||
* @param bool $reshares
|
||||
* @param int $limit The maximum number of returned results, -1 for all results
|
||||
* @param int $offset
|
||||
* @param bool $allowExpired also return expired shares
|
||||
* @return IShare[]
|
||||
* @since 9.0.0
|
||||
* @since 31.0.0 Added $allowExpired parameter
|
||||
*/
|
||||
public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0);
|
||||
public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0, $allowExpired = false);
|
||||
|
||||
/**
|
||||
* Get shares shared with $user.
|
||||
|
|
|
|||
|
|
@ -147,8 +147,9 @@
|
|||
<errorLevel type="suppress">
|
||||
<!-- Helper classes for sharing API integration from other apps -->
|
||||
<referencedClass name="OCA\Deck\Sharing\ShareAPIHelper" />
|
||||
<referencedClass name="OCA\Talk\Share\Helper\DeletedShareAPIController" />
|
||||
<referencedClass name="OCA\GlobalSiteSelector\Service\SlaveService"/>
|
||||
<referencedClass name="OCA\Talk\Share\Helper\DeletedShareAPIController" />
|
||||
<referencedClass name="OCA\Talk\Share\Helper\ShareAPIController" />
|
||||
</errorLevel>
|
||||
</UndefinedDocblockClass>
|
||||
<AmbiguousConstantInheritance>
|
||||
|
|
|
|||
|
|
@ -2951,13 +2951,13 @@ class ManagerTest extends \Test\TestCase {
|
|||
|
||||
public function testGetShareByTokenExpired(): void {
|
||||
$this->expectException(\OCP\Share\Exceptions\ShareNotFound::class);
|
||||
$this->expectExceptionMessage('The requested share does not exist anymore');
|
||||
$this->expectExceptionMessage('The requested share has expired');
|
||||
|
||||
$this->config
|
||||
->expects($this->once())
|
||||
->expects($this->exactly(2))
|
||||
->method('getAppValue')
|
||||
->with('core', 'shareapi_allow_links', 'yes')
|
||||
->willReturn('yes');
|
||||
->withConsecutive(['core', 'shareapi_allow_links', 'yes'], ['core', 'shareapi_delete_on_expire', 'yes'])
|
||||
->willReturnOnConsecutiveCalls('yes', 'yes');
|
||||
|
||||
$this->l->expects($this->once())
|
||||
->method('t')
|
||||
|
|
|
|||
Loading…
Reference in a new issue