diff options
Diffstat (limited to 'lib/private/Share20/DefaultShareProvider.php')
-rw-r--r-- | lib/private/Share20/DefaultShareProvider.php | 645 |
1 files changed, 365 insertions, 280 deletions
diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index c22c65f6c7b..5300e6e1baa 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -1,51 +1,29 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Jan-Philipp Litza <jplitza@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Maxence Lange <maxence@artificial-owl.com> - * @author phisch <git@philippschaffrath.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ + namespace OC\Share20; use OC\Files\Cache\Cache; use OC\Share20\Exception\BackendError; use OC\Share20\Exception\InvalidShare; use OC\Share20\Exception\ProviderException; +use OC\User\LazyUser; +use OCA\Files_Sharing\AppInfo\Application; use OCP\AppFramework\Utility\ITimeFactory; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Defaults; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Node; +use OCP\IConfig; use OCP\IDBConnection; use OCP\IGroupManager; +use OCP\IL10N; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; @@ -53,8 +31,11 @@ use OCP\L10N\IFactory; use OCP\Mail\IMailer; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IAttributes; +use OCP\Share\IManager; use OCP\Share\IShare; -use OCP\Share\IShareProvider; +use OCP\Share\IShareProviderSupportsAccept; +use OCP\Share\IShareProviderSupportsAllSharesInFolder; +use OCP\Share\IShareProviderWithNotification; use Psr\Log\LoggerInterface; use function str_starts_with; @@ -63,56 +44,21 @@ use function str_starts_with; * * @package OC\Share20 */ -class DefaultShareProvider implements IShareProvider { - // Special share type for user modified group shares - public const SHARE_TYPE_USERGROUP = 2; - - /** @var IDBConnection */ - private $dbConn; - - /** @var IUserManager */ - private $userManager; - - /** @var IGroupManager */ - private $groupManager; - - /** @var IRootFolder */ - private $rootFolder; - - /** @var IMailer */ - private $mailer; - - /** @var Defaults */ - private $defaults; - - /** @var IFactory */ - private $l10nFactory; - - /** @var IURLGenerator */ - private $urlGenerator; - - private ITimeFactory $timeFactory; - +class DefaultShareProvider implements IShareProviderWithNotification, IShareProviderSupportsAccept, IShareProviderSupportsAllSharesInFolder { public function __construct( - IDBConnection $connection, - IUserManager $userManager, - IGroupManager $groupManager, - IRootFolder $rootFolder, - IMailer $mailer, - Defaults $defaults, - IFactory $l10nFactory, - IURLGenerator $urlGenerator, - ITimeFactory $timeFactory, + private IDBConnection $dbConn, + private IUserManager $userManager, + private IGroupManager $groupManager, + private IRootFolder $rootFolder, + private IMailer $mailer, + private Defaults $defaults, + private IFactory $l10nFactory, + private IURLGenerator $urlGenerator, + private ITimeFactory $timeFactory, + private LoggerInterface $logger, + private IManager $shareManager, + private IConfig $config, ) { - $this->dbConn = $connection; - $this->userManager = $userManager; - $this->groupManager = $groupManager; - $this->rootFolder = $rootFolder; - $this->mailer = $mailer; - $this->defaults = $defaults; - $this->l10nFactory = $l10nFactory; - $this->urlGenerator = $urlGenerator; - $this->timeFactory = $timeFactory; } /** @@ -138,22 +84,30 @@ class DefaultShareProvider implements IShareProvider { $qb->insert('share'); $qb->setValue('share_type', $qb->createNamedParameter($share->getShareType())); + $expirationDate = $share->getExpirationDate(); + if ($expirationDate !== null) { + $expirationDate = clone $expirationDate; + $expirationDate->setTimezone(new \DateTimeZone(date_default_timezone_get())); + } + if ($share->getShareType() === IShare::TYPE_USER) { //Set the UID of the user we share with $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith())); $qb->setValue('accepted', $qb->createNamedParameter(IShare::STATUS_PENDING)); //If an expiration date is set store it - if ($share->getExpirationDate() !== null) { - $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime')); + if ($expirationDate !== null) { + $qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime')); } + + $qb->setValue('reminder_sent', $qb->createNamedParameter($share->getReminderSent(), IQueryBuilder::PARAM_BOOL)); } elseif ($share->getShareType() === IShare::TYPE_GROUP) { //Set the GID of the group we share with $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith())); //If an expiration date is set store it - if ($share->getExpirationDate() !== null) { - $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime')); + if ($expirationDate !== null) { + $qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime')); } } elseif ($share->getShareType() === IShare::TYPE_LINK) { //set label for public link @@ -169,13 +123,11 @@ class DefaultShareProvider implements IShareProvider { $qb->setValue('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL)); //If an expiration date is set store it - if ($share->getExpirationDate() !== null) { - $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime')); + if ($expirationDate !== null) { + $qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime')); } - if (method_exists($share, 'getParent')) { - $qb->setValue('parent', $qb->createNamedParameter($share->getParent())); - } + $qb->setValue('parent', $qb->createNamedParameter($share->getParent())); $qb->setValue('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0, IQueryBuilder::PARAM_INT)); } else { @@ -250,6 +202,12 @@ class DefaultShareProvider implements IShareProvider { $shareAttributes = $this->formatShareAttributes($share->getAttributes()); + $expirationDate = $share->getExpirationDate(); + if ($expirationDate !== null) { + $expirationDate = clone $expirationDate; + $expirationDate->setTimezone(new \DateTimeZone(date_default_timezone_get())); + } + if ($share->getShareType() === IShare::TYPE_USER) { /* * We allow updating the recipient on user shares. @@ -264,10 +222,11 @@ class DefaultShareProvider implements IShareProvider { ->set('attributes', $qb->createNamedParameter($shareAttributes)) ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) - ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) + ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE)) ->set('note', $qb->createNamedParameter($share->getNote())) ->set('accepted', $qb->createNamedParameter($share->getStatus())) - ->execute(); + ->set('reminder_sent', $qb->createNamedParameter($share->getReminderSent(), IQueryBuilder::PARAM_BOOL)) + ->executeStatement(); } elseif ($share->getShareType() === IShare::TYPE_GROUP) { $qb = $this->dbConn->getQueryBuilder(); $qb->update('share') @@ -278,9 +237,9 @@ class DefaultShareProvider implements IShareProvider { ->set('attributes', $qb->createNamedParameter($shareAttributes)) ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) - ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) + ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE)) ->set('note', $qb->createNamedParameter($share->getNote())) - ->execute(); + ->executeStatement(); /* * Update all user defined group shares @@ -293,9 +252,9 @@ class DefaultShareProvider implements IShareProvider { ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) - ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) + ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE)) ->set('note', $qb->createNamedParameter($share->getNote())) - ->execute(); + ->executeStatement(); /* * Now update the permissions for all children that have not set it to 0 @@ -306,7 +265,7 @@ class DefaultShareProvider implements IShareProvider { ->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0))) ->set('permissions', $qb->createNamedParameter($share->getPermissions())) ->set('attributes', $qb->createNamedParameter($shareAttributes)) - ->execute(); + ->executeStatement(); } elseif ($share->getShareType() === IShare::TYPE_LINK) { $qb = $this->dbConn->getQueryBuilder(); $qb->update('share') @@ -320,11 +279,11 @@ class DefaultShareProvider implements IShareProvider { ->set('item_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('token', $qb->createNamedParameter($share->getToken())) - ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) + ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE)) ->set('note', $qb->createNamedParameter($share->getNote())) ->set('label', $qb->createNamedParameter($share->getLabel())) - ->set('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0), IQueryBuilder::PARAM_INT) - ->execute(); + ->set('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0, IQueryBuilder::PARAM_INT)) + ->executeStatement(); } if ($originalShare->getNote() !== $share->getNote() && $share->getNote() !== '') { @@ -363,11 +322,8 @@ class DefaultShareProvider implements IShareProvider { ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))) ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )) - ->execute(); + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))) + ->executeQuery(); $data = $stmt->fetch(); $stmt->closeCursor(); @@ -395,19 +351,12 @@ class DefaultShareProvider implements IShareProvider { $qb->update('share') ->set('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED)) ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) - ->execute(); + ->executeStatement(); return $share; } - /** - * Get all children of this share - * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in - * - * @param \OCP\Share\IShare $parent - * @return \OCP\Share\IShare[] - */ - public function getChildren(\OCP\Share\IShare $parent) { + public function getChildren(IShare $parent): array { $children = []; $qb = $this->dbConn->getQueryBuilder(); @@ -424,13 +373,10 @@ class DefaultShareProvider implements IShareProvider { ], IQueryBuilder::PARAM_INT_ARRAY) ) ) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )) + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))) ->orderBy('id'); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); while ($data = $cursor->fetch()) { $children[] = $this->createShare($data); } @@ -457,7 +403,7 @@ class DefaultShareProvider implements IShareProvider { $qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))); } - $qb->execute(); + $qb->executeStatement(); } /** @@ -490,11 +436,8 @@ class DefaultShareProvider implements IShareProvider { ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))) ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )) - ->execute(); + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))) + ->executeQuery(); $data = $stmt->fetch(); @@ -516,7 +459,7 @@ class DefaultShareProvider implements IShareProvider { $qb->update('share') ->set('permissions', $qb->createNamedParameter(0)) ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) - ->execute(); + ->executeStatement(); } } elseif ($share->getShareType() === IShare::TYPE_USER) { if ($share->getSharedWith() !== $recipient) { @@ -533,6 +476,15 @@ class DefaultShareProvider implements IShareProvider { protected function createUserSpecificGroupShare(IShare $share, string $recipient): int { $type = $share->getNodeType(); + $shareFolder = $this->config->getSystemValue('share_folder', '/'); + $allowCustomShareFolder = $this->config->getSystemValueBool('sharing.allow_custom_share_folder', true); + if ($allowCustomShareFolder) { + $shareFolder = $this->config->getUserValue($recipient, Application::APP_ID, 'share_folder', $shareFolder); + } + + $target = $shareFolder . '/' . $share->getNode()->getName(); + $target = \OC\Files\Filesystem::normalizePath($target); + $qb = $this->dbConn->getQueryBuilder(); $qb->insert('share') ->values([ @@ -544,10 +496,10 @@ class DefaultShareProvider implements IShareProvider { 'item_type' => $qb->createNamedParameter($type), 'item_source' => $qb->createNamedParameter($share->getNodeId()), 'file_source' => $qb->createNamedParameter($share->getNodeId()), - 'file_target' => $qb->createNamedParameter($share->getTarget()), + 'file_target' => $qb->createNamedParameter($target), 'permissions' => $qb->createNamedParameter($share->getPermissions()), 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()), - ])->execute(); + ])->executeStatement(); return $qb->getLastInsertId(); } @@ -565,7 +517,7 @@ class DefaultShareProvider implements IShareProvider { ->where( $qb->expr()->eq('id', $qb->createNamedParameter($share->getId())) ); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $data = $cursor->fetch(); $cursor->closeCursor(); @@ -582,7 +534,7 @@ class DefaultShareProvider implements IShareProvider { $qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)) ); - $qb->execute(); + $qb->executeStatement(); return $this->getShareById($share->getId(), $recipient); } @@ -597,7 +549,7 @@ class DefaultShareProvider implements IShareProvider { $qb->update('share') ->set('file_target', $qb->createNamedParameter($share->getTarget())) ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) - ->execute(); + ->executeStatement(); } elseif ($share->getShareType() === IShare::TYPE_GROUP) { // Check if there is a usergroup share $qb = $this->dbConn->getQueryBuilder(); @@ -606,12 +558,9 @@ class DefaultShareProvider implements IShareProvider { ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))) ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )) + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))) ->setMaxResults(1) - ->execute(); + ->executeQuery(); $data = $stmt->fetch(); $stmt->closeCursor(); @@ -637,14 +586,14 @@ class DefaultShareProvider implements IShareProvider { 'permissions' => $qb->createNamedParameter($share->getPermissions()), 'attributes' => $qb->createNamedParameter($shareAttributes), 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()), - ])->execute(); + ])->executeStatement(); } else { // Already a usergroup share. Update it. $qb = $this->dbConn->getQueryBuilder(); $qb->update('share') ->set('file_target', $qb->createNamedParameter($share->getTarget())) ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id']))) - ->execute(); + ->executeStatement(); } } @@ -652,35 +601,45 @@ class DefaultShareProvider implements IShareProvider { } public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true) { + if (!$shallow) { + throw new \Exception('non-shallow getSharesInFolder is no longer supported'); + } + + return $this->getSharesInFolderInternal($userId, $node, $reshares); + } + + public function getAllSharesInFolder(Folder $node): array { + return $this->getSharesInFolderInternal(null, $node, null); + } + + /** + * @return array<int, list<IShare>> + */ + private function getSharesInFolderInternal(?string $userId, Folder $node, ?bool $reshares): array { $qb = $this->dbConn->getQueryBuilder(); $qb->select('s.*', 'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash', 'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime', 'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum') ->from('share', 's') - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )); + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))); - $qb->andWhere($qb->expr()->orX( - $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)), - $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)), - $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)) - )); + $qb->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK], IQueryBuilder::PARAM_INT_ARRAY))); - /** - * Reshares for this user are shares where they are the owner. - */ - if ($reshares === false) { - $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); - } else { - $qb->andWhere( - $qb->expr()->orX( - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) - ) - ); + if ($userId !== null) { + /** + * Reshares for this user are shares where they are the owner. + */ + if ($reshares !== true) { + $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); + } else { + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) + ) + ); + } } // todo? maybe get these from the oc_mounts table @@ -692,28 +651,12 @@ class DefaultShareProvider implements IShareProvider { }, $childMountNodes); $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')); - $storageFilter = $qb->expr()->eq('f.storage', $qb->createNamedParameter($node->getMountPoint()->getNumericStorageId(), IQueryBuilder::PARAM_INT)); - if ($shallow) { - $qb->andWhere( - $qb->expr()->orX( - $qb->expr()->andX( - $storageFilter, - $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())), - ), - $qb->expr()->in('f.fileid', $qb->createParameter('chunk')) - ) - ); - } else { - $qb->andWhere( - $qb->expr()->orX( - $qb->expr()->andX( - $storageFilter, - $qb->expr()->like('f.path', $qb->createNamedParameter($this->dbConn->escapeLikeParameter($node->getInternalPath()) . '/%')), - ), - $qb->expr()->in('f.fileid', $qb->createParameter('chunk')) - ) - ); - } + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())), + $qb->expr()->in('f.fileid', $qb->createParameter('chunk')) + ) + ); $qb->orderBy('id'); @@ -745,10 +688,7 @@ class DefaultShareProvider implements IShareProvider { $qb = $this->dbConn->getQueryBuilder(); $qb->select('*') ->from('share') - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )); + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))); $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType))); @@ -779,7 +719,7 @@ class DefaultShareProvider implements IShareProvider { $qb->setFirstResult($offset); $qb->orderBy('id'); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $shares = []; while ($data = $cursor->fetch()) { $shares[] = $this->createShare($data); @@ -808,12 +748,9 @@ class DefaultShareProvider implements IShareProvider { ], IQueryBuilder::PARAM_INT_ARRAY) ) ) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )); + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $data = $cursor->fetch(); $cursor->closeCursor(); @@ -829,7 +766,7 @@ class DefaultShareProvider implements IShareProvider { // If the recipient is set for a group share resolve to that user if ($recipientId !== null && $share->getShareType() === IShare::TYPE_GROUP) { - $share = $this->resolveGroupShares([(int) $share->getId() => $share], $recipientId)[0]; + $share = $this->resolveGroupShares([(int)$share->getId() => $share], $recipientId)[0]; } return $share; @@ -847,17 +784,10 @@ class DefaultShareProvider implements IShareProvider { $cursor = $qb->select('*') ->from('share') ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) - ->andWhere( - $qb->expr()->orX( - $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)), - $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)) - ) - ) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )) - ->execute(); + ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK], IQueryBuilder::PARAM_INT_ARRAY))) + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))) + ->orderBy('id', 'ASC') + ->executeQuery(); $shares = []; while ($data = $cursor->fetch()) { @@ -924,23 +854,20 @@ class DefaultShareProvider implements IShareProvider { $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )); + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))); // Filter by node if provided if ($node !== null) { $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); } - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); while ($data = $cursor->fetch()) { if ($data['fileid'] && $data['path'] === null) { - $data['path'] = (string) $data['path']; - $data['name'] = (string) $data['name']; - $data['checksum'] = (string) $data['checksum']; + $data['path'] = (string)$data['path']; + $data['name'] = (string)$data['name']; + $data['checksum'] = (string)$data['checksum']; } if ($this->isAccessibleResult($data)) { $shares[] = $this->createShare($data); @@ -948,8 +875,8 @@ class DefaultShareProvider implements IShareProvider { } $cursor->closeCursor(); } elseif ($shareType === IShare::TYPE_GROUP) { - $user = $this->userManager->get($userId); - $allGroups = ($user instanceof IUser) ? $this->groupManager->getUserGroupIds($user) : []; + $user = new LazyUser($userId, $this->userManager); + $allGroups = $this->groupManager->getUserGroupIds($user); /** @var Share[] $shares2 */ $shares2 = []; @@ -985,7 +912,6 @@ class DefaultShareProvider implements IShareProvider { $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); } - $groups = array_filter($groups); $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))) @@ -993,12 +919,9 @@ class DefaultShareProvider implements IShareProvider { $groups, IQueryBuilder::PARAM_STR_ARRAY ))) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )); + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); while ($data = $cursor->fetch()) { if ($offset > 0) { $offset--; @@ -1039,11 +962,8 @@ class DefaultShareProvider implements IShareProvider { ->from('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK))) ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )) - ->execute(); + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))) + ->executeQuery(); $data = $cursor->fetch(); @@ -1061,7 +981,7 @@ class DefaultShareProvider implements IShareProvider { } /** - * Create a share object from an database row + * Create a share object from a database row * * @param mixed[] $data * @return \OCP\Share\IShare @@ -1076,7 +996,7 @@ class DefaultShareProvider implements IShareProvider { ->setNote((string)$data['note']) ->setMailSend((bool)$data['mail_send']) ->setStatus((int)$data['accepted']) - ->setLabel($data['label']); + ->setLabel($data['label'] ?? ''); $shareTime = new \DateTime(); $shareTime->setTimestamp((int)$data['stime']); @@ -1084,9 +1004,9 @@ class DefaultShareProvider implements IShareProvider { if ($share->getShareType() === IShare::TYPE_USER) { $share->setSharedWith($data['share_with']); - $user = $this->userManager->get($data['share_with']); - if ($user !== null) { - $share->setSharedWithDisplayName($user->getDisplayName()); + $displayName = $this->userManager->getDisplayName($data['share_with']); + if ($displayName !== null) { + $share->setSharedWithDisplayName($displayName); } } elseif ($share->getShareType() === IShare::TYPE_GROUP) { $share->setSharedWith($data['share_with']); @@ -1123,6 +1043,7 @@ class DefaultShareProvider implements IShareProvider { $share->setProviderId($this->identifier()); $share->setHideDownload((int)$data['hide_download'] === 1); + $share->setReminderSent((bool)$data['reminder_sent']); return $share; } @@ -1140,7 +1061,7 @@ class DefaultShareProvider implements IShareProvider { ->from('share') ->where($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))) ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))) - ->andWhere($qb->expr()->in('item_type', [$qb->createNamedParameter('file'), $qb->createNamedParameter('folder')])); + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))); // this is called with either all group shares or one group share. // for all shares it's easier to just only search by share_with, @@ -1150,7 +1071,7 @@ class DefaultShareProvider implements IShareProvider { $query->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))); } - $stmt = $query->execute(); + $stmt = $query->executeQuery(); while ($data = $stmt->fetch()) { if (array_key_exists($data['parent'], $shareMap)) { @@ -1197,10 +1118,7 @@ class DefaultShareProvider implements IShareProvider { */ $qb->where( $qb->expr()->andX( - $qb->expr()->orX( - $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)), - $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)) - ), + $qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_GROUP, IShare::TYPE_USERGROUP], IQueryBuilder::PARAM_INT_ARRAY)), $qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)) ) ); @@ -1226,11 +1144,11 @@ class DefaultShareProvider implements IShareProvider { ); } else { $e = new \InvalidArgumentException('Default share provider tried to delete all shares for type: ' . $shareType); - \OCP\Server::get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]); + $this->logger->error($e->getMessage(), ['exception' => $e]); return; } - $qb->execute(); + $qb->executeStatement(); } /** @@ -1249,7 +1167,7 @@ class DefaultShareProvider implements IShareProvider { ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid))); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $ids = []; while ($row = $cursor->fetch()) { $ids[] = (int)$row['id']; @@ -1258,11 +1176,15 @@ class DefaultShareProvider implements IShareProvider { if (!empty($ids)) { $chunks = array_chunk($ids, 100); + + $qb = $this->dbConn->getQueryBuilder(); + $qb->delete('share') + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))) + ->andWhere($qb->expr()->in('parent', $qb->createParameter('parents'))); + foreach ($chunks as $chunk) { - $qb->delete('share') - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))) - ->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY))); - $qb->execute(); + $qb->setParameter('parents', $chunk, IQueryBuilder::PARAM_INT_ARRAY); + $qb->executeStatement(); } } @@ -1273,7 +1195,7 @@ class DefaultShareProvider implements IShareProvider { $qb->delete('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid))); - $qb->execute(); + $qb->executeStatement(); } /** @@ -1281,6 +1203,7 @@ class DefaultShareProvider implements IShareProvider { * * @param string $uid * @param string $gid + * @return void */ public function userDeletedFromGroup($uid, $gid) { /* @@ -1292,7 +1215,7 @@ class DefaultShareProvider implements IShareProvider { ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))) ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($gid))); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $ids = []; while ($row = $cursor->fetch()) { $ids[] = (int)$row['id']; @@ -1301,15 +1224,58 @@ class DefaultShareProvider implements IShareProvider { if (!empty($ids)) { $chunks = array_chunk($ids, 100); + + /* + * Delete all special shares with this user for the found group shares + */ + $qb = $this->dbConn->getQueryBuilder(); + $qb->delete('share') + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))) + ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid))) + ->andWhere($qb->expr()->in('parent', $qb->createParameter('parents'))); + foreach ($chunks as $chunk) { - /* - * Delete all special shares with this users for the found group shares - */ - $qb->delete('share') - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))) - ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($uid))) - ->andWhere($qb->expr()->in('parent', $qb->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY))); - $qb->execute(); + $qb->setParameter('parents', $chunk, IQueryBuilder::PARAM_INT_ARRAY); + $qb->executeStatement(); + } + } + + if ($this->shareManager->shareWithGroupMembersOnly()) { + $user = $this->userManager->get($uid); + if ($user === null) { + return; + } + $userGroups = $this->groupManager->getUserGroupIds($user); + $userGroups = array_diff($userGroups, $this->shareManager->shareWithGroupMembersOnlyExcludeGroupsList()); + + // Delete user shares received by the user from users in the group. + $userReceivedShares = $this->shareManager->getSharedWith($uid, IShare::TYPE_USER, null, -1); + foreach ($userReceivedShares as $share) { + $owner = $this->userManager->get($share->getSharedBy()); + if ($owner === null) { + continue; + } + $ownerGroups = $this->groupManager->getUserGroupIds($owner); + $mutualGroups = array_intersect($userGroups, $ownerGroups); + + if (count($mutualGroups) === 0) { + $this->shareManager->deleteShare($share); + } + } + + // Delete user shares from the user to users in the group. + $userEmittedShares = $this->shareManager->getSharesBy($uid, IShare::TYPE_USER, null, true, -1); + foreach ($userEmittedShares as $share) { + $recipient = $this->userManager->get($share->getSharedWith()); + if ($recipient === null) { + continue; + } + $recipientGroups = $this->groupManager->getUserGroupIds($recipient); + $mutualGroups = array_intersect($userGroups, $recipientGroups); + + if (count($mutualGroups) === 0) { + $this->shareManager->deleteShare($share); + } } } } @@ -1325,27 +1291,32 @@ class DefaultShareProvider implements IShareProvider { $qb = $this->dbConn->getQueryBuilder(); - $or = $qb->expr()->orX( - $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)), - $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP)), - $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK)) - ); + $shareTypes = [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK]; if ($currentAccess) { - $or->add($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))); + $shareTypes[] = IShare::TYPE_USERGROUP; } $qb->select('id', 'parent', 'share_type', 'share_with', 'file_source', 'file_target', 'permissions') ->from('share') ->where( - $or + $qb->expr()->in('share_type', $qb->createNamedParameter($shareTypes, IQueryBuilder::PARAM_INT_ARRAY)) ) ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )); - $cursor = $qb->execute(); + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))); + + // Ensure accepted is true for user and usergroup type + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->andX( + $qb->expr()->neq('share_type', $qb->createNamedParameter(IShare::TYPE_USER)), + $qb->expr()->neq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)), + ), + $qb->expr()->eq('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED, IQueryBuilder::PARAM_INT)), + ), + ); + + $cursor = $qb->executeQuery(); $users = []; $link = false; @@ -1397,8 +1368,8 @@ class DefaultShareProvider implements IShareProvider { protected function filterSharesOfUser(array $shares) { // Group shares when the user has a share exception foreach ($shares as $id => $share) { - $type = (int) $share['share_type']; - $permissions = (int) $share['permissions']; + $type = (int)$share['share_type']; + $permissions = (int)$share['permissions']; if ($type === IShare::TYPE_USERGROUP) { unset($shares[$share['parent']]); @@ -1442,6 +1413,126 @@ class DefaultShareProvider implements IShareProvider { } } + public function sendMailNotification(IShare $share): bool { + try { + // Check user + $user = $this->userManager->get($share->getSharedWith()); + if ($user === null) { + $this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because user could not be found.', ['app' => 'share']); + return false; + } + + // Handle user shares + if ($share->getShareType() === IShare::TYPE_USER) { + // Check email address + $emailAddress = $user->getEMailAddress(); + if ($emailAddress === null || $emailAddress === '') { + $this->logger->debug('Share notification not sent to ' . $share->getSharedWith() . ' because email address is not set.', ['app' => 'share']); + return false; + } + + $userLang = $this->l10nFactory->getUserLanguage($user); + $l = $this->l10nFactory->get('lib', $userLang); + $this->sendUserShareMail( + $l, + $share->getNode()->getName(), + $this->urlGenerator->linkToRouteAbsolute('files_sharing.Accept.accept', ['shareId' => $share->getFullId()]), + $share->getSharedBy(), + $emailAddress, + $share->getExpirationDate(), + $share->getNote() + ); + $this->logger->debug('Sent share notification to ' . $emailAddress . ' for share with ID ' . $share->getId() . '.', ['app' => 'share']); + return true; + } + } catch (\Exception $e) { + $this->logger->error('Share notification mail could not be sent.', ['exception' => $e]); + } + + return false; + } + + /** + * Send mail notifications for the user share type + * + * @param IL10N $l Language of the recipient + * @param string $filename file/folder name + * @param string $link link to the file/folder + * @param string $initiator user ID of share sender + * @param string $shareWith email address of share receiver + * @param \DateTime|null $expiration + * @param string $note + * @throws \Exception + */ + protected function sendUserShareMail( + IL10N $l, + $filename, + $link, + $initiator, + $shareWith, + ?\DateTime $expiration = null, + $note = '') { + $initiatorUser = $this->userManager->get($initiator); + $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; + + $message = $this->mailer->createMessage(); + + $emailTemplate = $this->mailer->createEMailTemplate('files_sharing.RecipientNotification', [ + 'filename' => $filename, + 'link' => $link, + 'initiator' => $initiatorDisplayName, + 'expiration' => $expiration, + 'shareWith' => $shareWith, + ]); + + $emailTemplate->setSubject($l->t('%1$s shared %2$s with you', [$initiatorDisplayName, $filename])); + $emailTemplate->addHeader(); + $emailTemplate->addHeading($l->t('%1$s shared %2$s with you', [$initiatorDisplayName, $filename]), false); + + if ($note !== '') { + $emailTemplate->addBodyText(htmlspecialchars($note), $note); + } + + $emailTemplate->addBodyButton( + $l->t('Open %s', [$filename]), + $link + ); + + $message->setTo([$shareWith]); + + // The "From" contains the sharers name + $instanceName = $this->defaults->getName(); + $senderName = $l->t( + '%1$s via %2$s', + [ + $initiatorDisplayName, + $instanceName, + ] + ); + $message->setFrom([\OCP\Util::getDefaultEmailAddress('noreply') => $senderName]); + + // The "Reply-To" is set to the sharer if an mail address is configured + // also the default footer contains a "Do not reply" which needs to be adjusted. + if ($initiatorUser) { + $initiatorEmail = $initiatorUser->getEMailAddress(); + if ($initiatorEmail !== null) { + $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); + $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')); + } else { + $emailTemplate->addFooter(); + } + } else { + $emailTemplate->addFooter(); + } + + $message->useTemplate($emailTemplate); + $failedRecipients = $this->mailer->send($message); + if (!empty($failedRecipients)) { + $this->logger->error('Share notification mail could not be sent to: ' . implode(', ', $failedRecipients)); + return; + } + } + /** * send note by mail * @@ -1478,20 +1569,20 @@ class DefaultShareProvider implements IShareProvider { $initiatorUser = $this->userManager->get($initiator); $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; - $plainHeading = $l->t('%1$s shared »%2$s« with you and wants to add:', [$initiatorDisplayName, $filename]); - $htmlHeading = $l->t('%1$s shared »%2$s« with you and wants to add', [$initiatorDisplayName, $filename]); + $plainHeading = $l->t('%1$s shared %2$s with you and wants to add:', [$initiatorDisplayName, $filename]); + $htmlHeading = $l->t('%1$s shared %2$s with you and wants to add', [$initiatorDisplayName, $filename]); $message = $this->mailer->createMessage(); $emailTemplate = $this->mailer->createEMailTemplate('defaultShareProvider.sendNote'); - $emailTemplate->setSubject($l->t('»%s« added a note to a file shared with you', [$initiatorDisplayName])); + $emailTemplate->setSubject($l->t('%s added a note to a file shared with you', [$initiatorDisplayName])); $emailTemplate->addHeader(); $emailTemplate->addHeading($htmlHeading, $plainHeading); $emailTemplate->addBodyText(htmlspecialchars($note), $note); $link = $this->urlGenerator->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $share->getNode()->getId()]); $emailTemplate->addBodyButton( - $l->t('Open »%s«', [$filename]), + $l->t('Open %s', [$filename]), $link ); @@ -1529,15 +1620,9 @@ class DefaultShareProvider implements IShareProvider { $qb->select('*') ->from('share') - ->where( - $qb->expr()->orX( - $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_USER)), - $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_GROUP)), - $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_LINK)) - ) - ); + ->where($qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK], IQueryBuilder::PARAM_INT_ARRAY))); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); while ($data = $cursor->fetch()) { try { $share = $this->createShare($data); @@ -1555,7 +1640,7 @@ class DefaultShareProvider implements IShareProvider { * * @return IShare the modified share */ - private function updateShareAttributes(IShare $share, ?string $data): IShare { + protected function updateShareAttributes(IShare $share, ?string $data): IShare { if ($data !== null && $data !== '') { $attributes = new ShareAttributes(); $compressedAttributes = \json_decode($data, true); @@ -1578,7 +1663,7 @@ class DefaultShareProvider implements IShareProvider { /** * Format IAttributes to database format (JSON string) */ - private function formatShareAttributes(?IAttributes $attributes): ?string { + protected function formatShareAttributes(?IAttributes $attributes): ?string { if ($attributes === null || empty($attributes->toArray())) { return null; } @@ -1588,7 +1673,7 @@ class DefaultShareProvider implements IShareProvider { $compressedAttributes[] = [ 0 => $attribute['scope'], 1 => $attribute['key'], - 2 => $attribute['enabled'] + 2 => $attribute['value'] ]; } return \json_encode($compressedAttributes); |