aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_sharing/lib
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_sharing/lib')
-rw-r--r--apps/files_sharing/lib/Activity/Filter.php1
-rw-r--r--apps/files_sharing/lib/Activity/Providers/Base.php1
-rw-r--r--apps/files_sharing/lib/Activity/Providers/Downloads.php17
-rw-r--r--apps/files_sharing/lib/Activity/Providers/Groups.php1
-rw-r--r--apps/files_sharing/lib/Activity/Providers/PublicLinks.php1
-rw-r--r--apps/files_sharing/lib/Activity/Providers/RemoteShares.php1
-rw-r--r--apps/files_sharing/lib/Activity/Providers/Users.php1
-rw-r--r--apps/files_sharing/lib/Activity/Settings/PublicLinks.php1
-rw-r--r--apps/files_sharing/lib/Activity/Settings/PublicLinksUpload.php3
-rw-r--r--apps/files_sharing/lib/Activity/Settings/RemoteShare.php1
-rw-r--r--apps/files_sharing/lib/Activity/Settings/Shared.php1
-rw-r--r--apps/files_sharing/lib/AppInfo/Application.php16
-rw-r--r--apps/files_sharing/lib/Cache.php7
-rw-r--r--apps/files_sharing/lib/Capabilities.php29
-rw-r--r--apps/files_sharing/lib/Collaboration/ShareRecipientSorter.php1
-rw-r--r--apps/files_sharing/lib/Command/CleanupRemoteStorages.php1
-rw-r--r--apps/files_sharing/lib/Command/ExiprationNotification.php5
-rw-r--r--apps/files_sharing/lib/Command/ListShares.php161
-rw-r--r--apps/files_sharing/lib/Config/ConfigLexicon.php41
-rw-r--r--apps/files_sharing/lib/Controller/DeletedShareAPIController.php17
-rw-r--r--apps/files_sharing/lib/Controller/ExternalSharesController.php1
-rw-r--r--apps/files_sharing/lib/Controller/PublicPreviewController.php24
-rw-r--r--apps/files_sharing/lib/Controller/RemoteController.php1
-rw-r--r--apps/files_sharing/lib/Controller/ShareAPIController.php268
-rw-r--r--apps/files_sharing/lib/Controller/ShareController.php115
-rw-r--r--apps/files_sharing/lib/Controller/ShareInfoController.php1
-rw-r--r--apps/files_sharing/lib/Controller/ShareesAPIController.php19
-rw-r--r--apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php61
-rw-r--r--apps/files_sharing/lib/Exceptions/BrokenPath.php1
-rw-r--r--apps/files_sharing/lib/Exceptions/S2SException.php1
-rw-r--r--apps/files_sharing/lib/Exceptions/SharingRightsException.php1
-rw-r--r--apps/files_sharing/lib/ExpireSharesJob.php12
-rw-r--r--apps/files_sharing/lib/External/Cache.php1
-rw-r--r--apps/files_sharing/lib/External/Manager.php7
-rw-r--r--apps/files_sharing/lib/External/Mount.php1
-rw-r--r--apps/files_sharing/lib/External/MountProvider.php5
-rw-r--r--apps/files_sharing/lib/External/Scanner.php4
-rw-r--r--apps/files_sharing/lib/External/Storage.php4
-rw-r--r--apps/files_sharing/lib/External/Watcher.php1
-rw-r--r--apps/files_sharing/lib/Helper.php7
-rw-r--r--apps/files_sharing/lib/Hooks.php4
-rw-r--r--apps/files_sharing/lib/ISharedStorage.php1
-rw-r--r--apps/files_sharing/lib/Listener/BeforeNodeReadListener.php189
-rw-r--r--apps/files_sharing/lib/Listener/LoadAdditionalListener.php3
-rw-r--r--apps/files_sharing/lib/Listener/LoadPublicFileRequestAuthListener.php10
-rw-r--r--apps/files_sharing/lib/Listener/LoadSidebarListener.php14
-rw-r--r--apps/files_sharing/lib/Middleware/OCSShareAPIMiddleware.php1
-rw-r--r--apps/files_sharing/lib/Middleware/ShareInfoMiddleware.php1
-rw-r--r--apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php12
-rw-r--r--apps/files_sharing/lib/Migration/OwncloudGuestShareType.php5
-rw-r--r--apps/files_sharing/lib/Migration/SetPasswordColumn.php1
-rw-r--r--apps/files_sharing/lib/MountProvider.php28
-rw-r--r--apps/files_sharing/lib/Notification/Listener.php8
-rw-r--r--apps/files_sharing/lib/Notification/Notifier.php6
-rw-r--r--apps/files_sharing/lib/OrphanHelper.php3
-rw-r--r--apps/files_sharing/lib/ResponseDefinitions.php1
-rw-r--r--apps/files_sharing/lib/Scanner.php1
-rw-r--r--apps/files_sharing/lib/Settings/Personal.php11
-rw-r--r--apps/files_sharing/lib/ShareBackend/File.php5
-rw-r--r--apps/files_sharing/lib/ShareBackend/Folder.php7
-rw-r--r--apps/files_sharing/lib/SharedMount.php27
-rw-r--r--apps/files_sharing/lib/SharedStorage.php23
-rw-r--r--apps/files_sharing/lib/Updater.php16
-rw-r--r--apps/files_sharing/lib/ViewOnly.php1
64 files changed, 882 insertions, 338 deletions
diff --git a/apps/files_sharing/lib/Activity/Filter.php b/apps/files_sharing/lib/Activity/Filter.php
index 7dc2a46e70f..4f3c4a7c914 100644
--- a/apps/files_sharing/lib/Activity/Filter.php
+++ b/apps/files_sharing/lib/Activity/Filter.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/Activity/Providers/Base.php b/apps/files_sharing/lib/Activity/Providers/Base.php
index 464cb336042..7428af382fc 100644
--- a/apps/files_sharing/lib/Activity/Providers/Base.php
+++ b/apps/files_sharing/lib/Activity/Providers/Base.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/Activity/Providers/Downloads.php b/apps/files_sharing/lib/Activity/Providers/Downloads.php
index ac9522ef93b..bddf2d30f73 100644
--- a/apps/files_sharing/lib/Activity/Providers/Downloads.php
+++ b/apps/files_sharing/lib/Activity/Providers/Downloads.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -23,11 +24,11 @@ class Downloads extends Base {
public function parseShortVersion(IEvent $event) {
$parsedParameters = $this->getParsedParameters($event);
- if ($event->getSubject() === self::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED ||
- $event->getSubject() === self::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED) {
+ if ($event->getSubject() === self::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED
+ || $event->getSubject() === self::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED) {
$subject = $this->l->t('Downloaded via public link');
- } elseif ($event->getSubject() === self::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED ||
- $event->getSubject() === self::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED) {
+ } elseif ($event->getSubject() === self::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED
+ || $event->getSubject() === self::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED) {
$subject = $this->l->t('Downloaded by {email}');
} else {
throw new \InvalidArgumentException();
@@ -53,8 +54,8 @@ class Downloads extends Base {
public function parseLongVersion(IEvent $event, ?IEvent $previousEvent = null) {
$parsedParameters = $this->getParsedParameters($event);
- if ($event->getSubject() === self::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED ||
- $event->getSubject() === self::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED) {
+ if ($event->getSubject() === self::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED
+ || $event->getSubject() === self::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED) {
if (!isset($parsedParameters['remote-address-hash']['type'])) {
$subject = $this->l->t('{file} downloaded via public link');
$this->setSubjects($event, $subject, $parsedParameters);
@@ -63,8 +64,8 @@ class Downloads extends Base {
$this->setSubjects($event, $subject, $parsedParameters);
$event = $this->eventMerger->mergeEvents('file', $event, $previousEvent);
}
- } elseif ($event->getSubject() === self::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED ||
- $event->getSubject() === self::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED) {
+ } elseif ($event->getSubject() === self::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED
+ || $event->getSubject() === self::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED) {
$subject = $this->l->t('{email} downloaded {file}');
$this->setSubjects($event, $subject, $parsedParameters);
} else {
diff --git a/apps/files_sharing/lib/Activity/Providers/Groups.php b/apps/files_sharing/lib/Activity/Providers/Groups.php
index b64104739db..d0086c05ced 100644
--- a/apps/files_sharing/lib/Activity/Providers/Groups.php
+++ b/apps/files_sharing/lib/Activity/Providers/Groups.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/Activity/Providers/PublicLinks.php b/apps/files_sharing/lib/Activity/Providers/PublicLinks.php
index 6cf5c05d874..15ffaf2cdb0 100644
--- a/apps/files_sharing/lib/Activity/Providers/PublicLinks.php
+++ b/apps/files_sharing/lib/Activity/Providers/PublicLinks.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/Activity/Providers/RemoteShares.php b/apps/files_sharing/lib/Activity/Providers/RemoteShares.php
index 1971f935d30..750d0747b62 100644
--- a/apps/files_sharing/lib/Activity/Providers/RemoteShares.php
+++ b/apps/files_sharing/lib/Activity/Providers/RemoteShares.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/Activity/Providers/Users.php b/apps/files_sharing/lib/Activity/Providers/Users.php
index 6c136d1f383..5c833ffae93 100644
--- a/apps/files_sharing/lib/Activity/Providers/Users.php
+++ b/apps/files_sharing/lib/Activity/Providers/Users.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/Activity/Settings/PublicLinks.php b/apps/files_sharing/lib/Activity/Settings/PublicLinks.php
index 12a543f1e7f..0d3d00d2a7b 100644
--- a/apps/files_sharing/lib/Activity/Settings/PublicLinks.php
+++ b/apps/files_sharing/lib/Activity/Settings/PublicLinks.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/Activity/Settings/PublicLinksUpload.php b/apps/files_sharing/lib/Activity/Settings/PublicLinksUpload.php
index 00bf7d0dde9..fd55752632d 100644
--- a/apps/files_sharing/lib/Activity/Settings/PublicLinksUpload.php
+++ b/apps/files_sharing/lib/Activity/Settings/PublicLinksUpload.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -61,6 +62,6 @@ class PublicLinksUpload extends ShareActivitySettings {
* @since 11.0.0
*/
public function isDefaultEnabledMail() {
- return true;
+ return false;
}
}
diff --git a/apps/files_sharing/lib/Activity/Settings/RemoteShare.php b/apps/files_sharing/lib/Activity/Settings/RemoteShare.php
index 339e634638b..c04364bef20 100644
--- a/apps/files_sharing/lib/Activity/Settings/RemoteShare.php
+++ b/apps/files_sharing/lib/Activity/Settings/RemoteShare.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/Activity/Settings/Shared.php b/apps/files_sharing/lib/Activity/Settings/Shared.php
index 30b5e68b04c..3717512eebd 100644
--- a/apps/files_sharing/lib/Activity/Settings/Shared.php
+++ b/apps/files_sharing/lib/Activity/Settings/Shared.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/AppInfo/Application.php b/apps/files_sharing/lib/AppInfo/Application.php
index 6c0d5ca0781..8ddb3afaf33 100644
--- a/apps/files_sharing/lib/AppInfo/Application.php
+++ b/apps/files_sharing/lib/AppInfo/Application.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -12,10 +13,12 @@ use OC\User\DisplayNameCache;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCA\Files\Event\LoadSidebar;
use OCA\Files_Sharing\Capabilities;
+use OCA\Files_Sharing\Config\ConfigLexicon;
use OCA\Files_Sharing\External\Manager;
use OCA\Files_Sharing\External\MountProvider as ExternalMountProvider;
use OCA\Files_Sharing\Helper;
use OCA\Files_Sharing\Listener\BeforeDirectFileDownloadListener;
+use OCA\Files_Sharing\Listener\BeforeNodeReadListener;
use OCA\Files_Sharing\Listener\BeforeZipCreatedListener;
use OCA\Files_Sharing\Listener\LoadAdditionalListener;
use OCA\Files_Sharing\Listener\LoadPublicFileRequestAuthListener;
@@ -42,6 +45,7 @@ use OCP\Federation\ICloudIdManager;
use OCP\Files\Config\IMountProviderCollection;
use OCP\Files\Events\BeforeDirectFileDownloadEvent;
use OCP\Files\Events\BeforeZipCreatedEvent;
+use OCP\Files\Events\Node\BeforeNodeReadEvent;
use OCP\Group\Events\GroupChangedEvent;
use OCP\Group\Events\GroupDeletedEvent;
use OCP\Group\Events\UserAddedEvent;
@@ -94,12 +98,18 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(ShareCreatedEvent::class, UserShareAcceptanceListener::class);
$context->registerEventListener(UserAddedEvent::class, UserAddedToGroupListener::class);
- // Handle download events for view only checks
- $context->registerEventListener(BeforeZipCreatedEvent::class, BeforeZipCreatedListener::class);
- $context->registerEventListener(BeforeDirectFileDownloadEvent::class, BeforeDirectFileDownloadListener::class);
+ // Publish activity for public download
+ $context->registerEventListener(BeforeNodeReadEvent::class, BeforeNodeReadListener::class);
+ $context->registerEventListener(BeforeZipCreatedEvent::class, BeforeNodeReadListener::class);
+
+ // Handle download events for view only checks. Priority higher than 0 to run early.
+ $context->registerEventListener(BeforeZipCreatedEvent::class, BeforeZipCreatedListener::class, 5);
+ $context->registerEventListener(BeforeDirectFileDownloadEvent::class, BeforeDirectFileDownloadListener::class, 5);
// File request auth
$context->registerEventListener(BeforeTemplateRenderedEvent::class, LoadPublicFileRequestAuthListener::class);
+
+ $context->registerConfigLexicon(ConfigLexicon::class);
}
public function boot(IBootContext $context): void {
diff --git a/apps/files_sharing/lib/Cache.php b/apps/files_sharing/lib/Cache.php
index 350f4718712..f9042fc0765 100644
--- a/apps/files_sharing/lib/Cache.php
+++ b/apps/files_sharing/lib/Cache.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -68,7 +69,7 @@ class Cache extends CacheJail {
return $this->root;
}
- protected function getGetUnjailedRoot(): string {
+ public function getGetUnjailedRoot(): string {
return $this->sourceRootInfo->getPath();
}
@@ -191,4 +192,8 @@ class Cache extends CacheJail {
return null;
}
}
+
+ public function markRootChanged(): void {
+ $this->rootUnchanged = false;
+ }
}
diff --git a/apps/files_sharing/lib/Capabilities.php b/apps/files_sharing/lib/Capabilities.php
index 1f491216abe..cbb9b5cd2f2 100644
--- a/apps/files_sharing/lib/Capabilities.php
+++ b/apps/files_sharing/lib/Capabilities.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -6,6 +7,7 @@
*/
namespace OCA\Files_Sharing;
+use OCP\App\IAppManager;
use OCP\Capabilities\ICapability;
use OCP\Constants;
use OCP\IConfig;
@@ -17,10 +19,10 @@ use OCP\Share\IManager;
* @package OCA\Files_Sharing
*/
class Capabilities implements ICapability {
-
public function __construct(
private IConfig $config,
private IManager $shareManager,
+ private IAppManager $appManager,
) {
}
@@ -157,14 +159,23 @@ class Capabilities implements ICapability {
}
//Federated sharing
- $res['federation'] = [
- 'outgoing' => $this->shareManager->outgoingServer2ServerSharesAllowed(),
- 'incoming' => $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'yes',
- // old bogus one, expire_date was not working before, keeping for compatibility
- 'expire_date' => ['enabled' => true],
- // the real deal, signifies that expiration date can be set on federated shares
- 'expire_date_supported' => ['enabled' => true],
- ];
+ if ($this->appManager->isEnabledForAnyone('federation')) {
+ $res['federation'] = [
+ 'outgoing' => $this->shareManager->outgoingServer2ServerSharesAllowed(),
+ 'incoming' => $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') === 'yes',
+ // old bogus one, expire_date was not working before, keeping for compatibility
+ 'expire_date' => ['enabled' => true],
+ // the real deal, signifies that expiration date can be set on federated shares
+ 'expire_date_supported' => ['enabled' => true],
+ ];
+ } else {
+ $res['federation'] = [
+ 'outgoing' => false,
+ 'incoming' => false,
+ 'expire_date' => ['enabled' => false],
+ 'expire_date_supported' => ['enabled' => false],
+ ];
+ }
// Sharee searches
$res['sharee'] = [
diff --git a/apps/files_sharing/lib/Collaboration/ShareRecipientSorter.php b/apps/files_sharing/lib/Collaboration/ShareRecipientSorter.php
index 993dba64888..803dfd6325f 100644
--- a/apps/files_sharing/lib/Collaboration/ShareRecipientSorter.php
+++ b/apps/files_sharing/lib/Collaboration/ShareRecipientSorter.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/Command/CleanupRemoteStorages.php b/apps/files_sharing/lib/Command/CleanupRemoteStorages.php
index 7e21768afee..809481e5c0f 100644
--- a/apps/files_sharing/lib/Command/CleanupRemoteStorages.php
+++ b/apps/files_sharing/lib/Command/CleanupRemoteStorages.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud GmbH.
diff --git a/apps/files_sharing/lib/Command/ExiprationNotification.php b/apps/files_sharing/lib/Command/ExiprationNotification.php
index df5656a434e..b7ea5c5f14e 100644
--- a/apps/files_sharing/lib/Command/ExiprationNotification.php
+++ b/apps/files_sharing/lib/Command/ExiprationNotification.php
@@ -8,6 +8,7 @@ declare(strict_types=1);
*/
namespace OCA\Files_Sharing\Command;
+use OCA\Files_Sharing\OrphanHelper;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IDBConnection;
use OCP\Notification\IManager as NotificationManager;
@@ -23,6 +24,7 @@ class ExiprationNotification extends Command {
private NotificationManager $notificationManager,
private IDBConnection $connection,
private ShareManager $shareManager,
+ private OrphanHelper $orphanHelper,
) {
parent::__construct();
}
@@ -50,7 +52,8 @@ class ExiprationNotification extends Command {
foreach ($shares as $share) {
if ($share->getExpirationDate() === null
|| $share->getExpirationDate()->getTimestamp() < $minTime->getTimestamp()
- || $share->getExpirationDate()->getTimestamp() > $maxTime->getTimestamp()) {
+ || $share->getExpirationDate()->getTimestamp() > $maxTime->getTimestamp()
+ || !$this->orphanHelper->isShareValid($share->getSharedBy(), $share->getNodeId())) {
continue;
}
diff --git a/apps/files_sharing/lib/Command/ListShares.php b/apps/files_sharing/lib/Command/ListShares.php
new file mode 100644
index 00000000000..2d5cdbf7812
--- /dev/null
+++ b/apps/files_sharing/lib/Command/ListShares.php
@@ -0,0 +1,161 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2025 Robin Appelman <robin@icewind.nl>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Files_Sharing\Command;
+
+use OC\Core\Command\Base;
+use OCP\Files\Folder;
+use OCP\Files\IRootFolder;
+use OCP\Files\Node;
+use OCP\Files\NotFoundException;
+use OCP\Share\IManager;
+use OCP\Share\IShare;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ListShares extends Base {
+ /** @var array<string, Node> */
+ private array $fileCache = [];
+
+ private const SHARE_TYPE_NAMES = [
+ IShare::TYPE_USER => 'user',
+ IShare::TYPE_GROUP => 'group',
+ IShare::TYPE_LINK => 'link',
+ IShare::TYPE_EMAIL => 'email',
+ IShare::TYPE_REMOTE => 'remote',
+ IShare::TYPE_REMOTE_GROUP => 'group',
+ IShare::TYPE_ROOM => 'room',
+ IShare::TYPE_DECK => 'deck',
+ ];
+
+ public function __construct(
+ private readonly IManager $shareManager,
+ private readonly IRootFolder $rootFolder,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure() {
+ parent::configure();
+ $this
+ ->setName('share:list')
+ ->setDescription('List available shares')
+ ->addOption('owner', null, InputOption::VALUE_REQUIRED, 'only show shares owned by a specific user')
+ ->addOption('recipient', null, InputOption::VALUE_REQUIRED, 'only show shares with a specific recipient')
+ ->addOption('by', null, InputOption::VALUE_REQUIRED, 'only show shares with by as specific user')
+ ->addOption('file', null, InputOption::VALUE_REQUIRED, 'only show shares of a specific file')
+ ->addOption('parent', null, InputOption::VALUE_REQUIRED, 'only show shares of files inside a specific folder')
+ ->addOption('recursive', null, InputOption::VALUE_NONE, 'also show shares nested deep inside the specified parent folder')
+ ->addOption('type', null, InputOption::VALUE_REQUIRED, 'only show shares of a specific type')
+ ->addOption('status', null, InputOption::VALUE_REQUIRED, 'only show shares with a specific status');
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output): int {
+ if ($input->getOption('recursive') && !$input->getOption('parent')) {
+ $output->writeln("<error>recursive option can't be used without parent option</error>");
+ return 1;
+ }
+
+ // todo: do some pre-filtering instead of first querying all shares
+ /** @var \Iterator<IShare> $allShares */
+ $allShares = $this->shareManager->getAllShares();
+ $shares = new \CallbackFilterIterator($allShares, function (IShare $share) use ($input) {
+ return $this->shouldShowShare($input, $share);
+ });
+ $shares = iterator_to_array($shares);
+ $data = array_map(function (IShare $share) {
+ return [
+ 'id' => $share->getId(),
+ 'file' => $share->getNodeId(),
+ 'target-path' => $share->getTarget(),
+ 'source-path' => $share->getNode()->getPath(),
+ 'owner' => $share->getShareOwner(),
+ 'recipient' => $share->getSharedWith(),
+ 'by' => $share->getSharedBy(),
+ 'type' => self::SHARE_TYPE_NAMES[$share->getShareType()] ?? 'unknown',
+ ];
+ }, $shares);
+
+ $this->writeTableInOutputFormat($input, $output, $data);
+ return 0;
+ }
+
+ private function getFileId(string $file): int {
+ if (is_numeric($file)) {
+ return (int)$file;
+ }
+ return $this->getFile($file)->getId();
+ }
+
+ private function getFile(string $file): Node {
+ if (isset($this->fileCache[$file])) {
+ return $this->fileCache[$file];
+ }
+
+ if (is_numeric($file)) {
+ $node = $this->rootFolder->getFirstNodeById((int)$file);
+ if (!$node) {
+ throw new NotFoundException("File with id $file not found");
+ }
+ } else {
+ $node = $this->rootFolder->get($file);
+ }
+ $this->fileCache[$file] = $node;
+ return $node;
+ }
+
+ private function getShareType(string $type): int {
+ foreach (self::SHARE_TYPE_NAMES as $shareType => $shareTypeName) {
+ if ($shareTypeName === $type) {
+ return $shareType;
+ }
+ }
+ throw new \Exception("Unknown share type $type");
+ }
+
+ private function shouldShowShare(InputInterface $input, IShare $share): bool {
+ if ($input->getOption('owner') && $share->getShareOwner() !== $input->getOption('owner')) {
+ return false;
+ }
+ if ($input->getOption('recipient') && $share->getSharedWith() !== $input->getOption('recipient')) {
+ return false;
+ }
+ if ($input->getOption('by') && $share->getSharedBy() !== $input->getOption('by')) {
+ return false;
+ }
+ if ($input->getOption('file') && $share->getNodeId() !== $this->getFileId($input->getOption('file'))) {
+ return false;
+ }
+ if ($input->getOption('parent')) {
+ $parent = $this->getFile($input->getOption('parent'));
+ if (!$parent instanceof Folder) {
+ throw new \Exception("Parent {$parent->getPath()} is not a folder");
+ }
+ $recursive = $input->getOption('recursive');
+ if (!$recursive) {
+ $shareCacheEntry = $share->getNodeCacheEntry();
+ if (!$shareCacheEntry) {
+ $shareCacheEntry = $share->getNode();
+ }
+ if ($shareCacheEntry->getParentId() !== $parent->getId()) {
+ return false;
+ }
+ } else {
+ $shareNode = $share->getNode();
+ if ($parent->getRelativePath($shareNode->getPath()) === null) {
+ return false;
+ }
+ }
+ }
+ if ($input->getOption('type') && $share->getShareType() !== $this->getShareType($input->getOption('type'))) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/apps/files_sharing/lib/Config/ConfigLexicon.php b/apps/files_sharing/lib/Config/ConfigLexicon.php
new file mode 100644
index 00000000000..c2743a2c4ce
--- /dev/null
+++ b/apps/files_sharing/lib/Config/ConfigLexicon.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Files_Sharing\Config;
+
+use OCP\Config\Lexicon\Entry;
+use OCP\Config\Lexicon\ILexicon;
+use OCP\Config\Lexicon\Strictness;
+use OCP\Config\ValueType;
+
+/**
+ * Config Lexicon for files_sharing.
+ *
+ * Please Add & Manage your Config Keys in that file and keep the Lexicon up to date!
+ *
+ * {@see ILexicon}
+ */
+class ConfigLexicon implements ILexicon {
+ public const SHOW_FEDERATED_AS_INTERNAL = 'show_federated_shares_as_internal';
+ public const SHOW_FEDERATED_TO_TRUSTED_AS_INTERNAL = 'show_federated_shares_to_trusted_servers_as_internal';
+
+ public function getStrictness(): Strictness {
+ return Strictness::IGNORE;
+ }
+
+ public function getAppConfigs(): array {
+ return [
+ new Entry(self::SHOW_FEDERATED_AS_INTERNAL, ValueType::BOOL, false, 'shows federated shares as internal shares', true),
+ new Entry(self::SHOW_FEDERATED_TO_TRUSTED_AS_INTERNAL, ValueType::BOOL, false, 'shows federated shares to trusted servers as internal shares', true),
+ ];
+ }
+
+ public function getUserConfigs(): array {
+ return [];
+ }
+}
diff --git a/apps/files_sharing/lib/Controller/DeletedShareAPIController.php b/apps/files_sharing/lib/Controller/DeletedShareAPIController.php
index fcd33bd88e6..2fa4d7c668f 100644
--- a/apps/files_sharing/lib/Controller/DeletedShareAPIController.php
+++ b/apps/files_sharing/lib/Controller/DeletedShareAPIController.php
@@ -8,6 +8,7 @@ declare(strict_types=1);
*/
namespace OCA\Files_Sharing\Controller;
+use OCA\Deck\Sharing\ShareAPIHelper;
use OCA\Files_Sharing\ResponseDefinitions;
use OCP\App\IAppManager;
use OCP\AppFramework\Http;
@@ -22,8 +23,8 @@ use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\IGroupManager;
use OCP\IRequest;
-use OCP\IServerContainer;
use OCP\IUserManager;
+use OCP\Server;
use OCP\Share\Exceptions\GenericShareException;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager as ShareManager;
@@ -43,7 +44,6 @@ class DeletedShareAPIController extends OCSController {
private IGroupManager $groupManager,
private IRootFolder $rootFolder,
private IAppManager $appManager,
- private IServerContainer $serverContainer,
) {
parent::__construct($appName, $request);
}
@@ -142,11 +142,12 @@ class DeletedShareAPIController extends OCSController {
#[NoAdminRequired]
public function index(): DataResponse {
$groupShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_GROUP, null, -1, 0);
+ $teamShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_CIRCLE, null, -1, 0);
$roomShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_ROOM, null, -1, 0);
$deckShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_DECK, null, -1, 0);
$sciencemeshShares = $this->shareManager->getDeletedSharedWith($this->userId, IShare::TYPE_SCIENCEMESH, null, -1, 0);
- $shares = array_merge($groupShares, $roomShares, $deckShares, $sciencemeshShares);
+ $shares = array_merge($groupShares, $teamShares, $roomShares, $deckShares, $sciencemeshShares);
$shares = array_values(array_map(function (IShare $share) {
return $this->formatShare($share);
@@ -200,7 +201,7 @@ class DeletedShareAPIController extends OCSController {
throw new QueryException();
}
- return $this->serverContainer->get('\OCA\Talk\Share\Helper\DeletedShareAPIController');
+ return Server::get('\OCA\Talk\Share\Helper\DeletedShareAPIController');
}
/**
@@ -209,7 +210,7 @@ class DeletedShareAPIController extends OCSController {
* If the Deck application is not enabled or the helper is not available
* a QueryException is thrown instead.
*
- * @return \OCA\Deck\Sharing\ShareAPIHelper
+ * @return ShareAPIHelper
* @throws QueryException
*/
private function getDeckShareHelper() {
@@ -217,7 +218,7 @@ class DeletedShareAPIController extends OCSController {
throw new QueryException();
}
- return $this->serverContainer->get('\OCA\Deck\Sharing\ShareAPIHelper');
+ return Server::get('\OCA\Deck\Sharing\ShareAPIHelper');
}
/**
@@ -226,7 +227,7 @@ class DeletedShareAPIController extends OCSController {
* If the sciencemesh application is not enabled or the helper is not available
* a QueryException is thrown instead.
*
- * @return \OCA\Deck\Sharing\ShareAPIHelper
+ * @return ShareAPIHelper
* @throws QueryException
*/
private function getSciencemeshShareHelper() {
@@ -234,6 +235,6 @@ class DeletedShareAPIController extends OCSController {
throw new QueryException();
}
- return $this->serverContainer->get('\OCA\ScienceMesh\Sharing\ShareAPIHelper');
+ return Server::get('\OCA\ScienceMesh\Sharing\ShareAPIHelper');
}
}
diff --git a/apps/files_sharing/lib/Controller/ExternalSharesController.php b/apps/files_sharing/lib/Controller/ExternalSharesController.php
index 9d15b03c6cd..fa828a9d2c2 100644
--- a/apps/files_sharing/lib/Controller/ExternalSharesController.php
+++ b/apps/files_sharing/lib/Controller/ExternalSharesController.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/apps/files_sharing/lib/Controller/PublicPreviewController.php b/apps/files_sharing/lib/Controller/PublicPreviewController.php
index 91dead57e80..d917f6e0ebb 100644
--- a/apps/files_sharing/lib/Controller/PublicPreviewController.php
+++ b/apps/files_sharing/lib/Controller/PublicPreviewController.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -11,6 +12,7 @@ use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
+use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\PublicShareController;
use OCP\Constants;
use OCP\Files\Folder;
@@ -18,6 +20,7 @@ use OCP\Files\NotFoundException;
use OCP\IPreview;
use OCP\IRequest;
use OCP\ISession;
+use OCP\Preview\IMimeIconProvider;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager as ShareManager;
use OCP\Share\IShare;
@@ -33,6 +36,7 @@ class PublicPreviewController extends PublicShareController {
private ShareManager $shareManager,
ISession $session,
private IPreview $previewManager,
+ private IMimeIconProvider $mimeIconProvider,
) {
parent::__construct($appName, $request, $session);
}
@@ -63,9 +67,11 @@ class PublicPreviewController extends PublicShareController {
* @param int $x Width of the preview
* @param int $y Height of the preview
* @param bool $a Whether to not crop the preview
- * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, list<empty>, array{}>
+ * @param bool $mimeFallback Whether to fallback to the mime icon if no preview is available
+ * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, list<empty>, array{}>|RedirectResponse<Http::STATUS_SEE_OTHER, array{}>
*
* 200: Preview returned
+ * 303: Redirect to the mime icon url if mimeFallback is true
* 400: Getting preview is not possible
* 403: Getting preview is not allowed
* 404: Share or preview not found
@@ -79,6 +85,7 @@ class PublicPreviewController extends PublicShareController {
int $x = 32,
int $y = 32,
$a = false,
+ bool $mimeFallback = false,
) {
$cacheForSeconds = 60 * 60 * 24; // 1 day
@@ -96,12 +103,12 @@ class PublicPreviewController extends PublicShareController {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
- $attributes = $share->getAttributes();
// Only explicitly set to false will forbid the download!
- $downloadForbidden = $attributes?->getAttribute('permissions', 'download') === false;
+ $downloadForbidden = !$share->canSeeContent();
+
// Is this header is set it means our UI is doing a preview for no-download shares
// we check a header so we at least prevent people from using the link directly (obfuscation)
- $isPublicPreview = $this->request->getHeader('X-NC-Preview') === 'true';
+ $isPublicPreview = $this->request->getHeader('x-nc-preview') === 'true';
if ($isPublicPreview && $downloadForbidden) {
// Only cache for 15 minutes on public preview requests to quickly remove from cache
@@ -124,6 +131,12 @@ class PublicPreviewController extends PublicShareController {
$response->cacheFor($cacheForSeconds);
return $response;
} catch (NotFoundException $e) {
+ // If we have no preview enabled, we can redirect to the mime icon if any
+ if ($mimeFallback) {
+ if ($url = $this->mimeIconProvider->getMimeIconUrl($file->getMimeType())) {
+ return new RedirectResponse($url);
+ }
+ }
return new DataResponse([], Http::STATUS_NOT_FOUND);
} catch (\InvalidArgumentException $e) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
@@ -169,8 +182,7 @@ class PublicPreviewController extends PublicShareController {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
- $attributes = $share->getAttributes();
- if ($attributes !== null && $attributes->getAttribute('permissions', 'download') === false) {
+ if (!$share->canSeeContent()) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
diff --git a/apps/files_sharing/lib/Controller/RemoteController.php b/apps/files_sharing/lib/Controller/RemoteController.php
index e23ae51f219..8c15cd8463e 100644
--- a/apps/files_sharing/lib/Controller/RemoteController.php
+++ b/apps/files_sharing/lib/Controller/RemoteController.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php
index 1fb62480049..095a8a75963 100644
--- a/apps/files_sharing/lib/Controller/ShareAPIController.php
+++ b/apps/files_sharing/lib/Controller/ShareAPIController.php
@@ -10,8 +10,12 @@ declare(strict_types=1);
namespace OCA\Files_Sharing\Controller;
use Exception;
+use OC\Core\AppInfo\ConfigLexicon;
+use OC\Files\FileInfo;
use OC\Files\Storage\Wrapper\Wrapper;
use OCA\Circles\Api\v1\Circles;
+use OCA\Deck\Sharing\ShareAPIHelper;
+use OCA\Federation\TrustedServers;
use OCA\Files\Helper;
use OCA\Files_Sharing\Exceptions\SharingRightsException;
use OCA\Files_Sharing\External\Storage;
@@ -39,18 +43,21 @@ use OCP\Files\Mount\IShareOwnerlessMount;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\HintException;
+use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IDateTimeZone;
use OCP\IGroupManager;
use OCP\IL10N;
use OCP\IPreview;
use OCP\IRequest;
+use OCP\ITagManager;
use OCP\IURLGenerator;
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\Exceptions\ShareTokenException;
use OCP\Share\IManager;
@@ -70,6 +77,7 @@ use Psr\Log\LoggerInterface;
class ShareAPIController extends OCSController {
private ?Node $lockedNode = null;
+ private array $trustedServerCache = [];
/**
* Share20OCS constructor.
@@ -84,6 +92,7 @@ class ShareAPIController extends OCSController {
private IURLGenerator $urlGenerator,
private IL10N $l,
private IConfig $config,
+ private IAppConfig $appConfig,
private IAppManager $appManager,
private ContainerInterface $serverContainer,
private IUserStatusManager $userStatusManager,
@@ -92,6 +101,8 @@ class ShareAPIController extends OCSController {
private LoggerInterface $logger,
private IProviderFactory $factory,
private IMailer $mailer,
+ private ITagManager $tagManager,
+ private ?TrustedServers $trustedServers,
private ?string $userId = null,
) {
parent::__construct($appName, $request);
@@ -194,6 +205,32 @@ class ShareAPIController extends OCSController {
$result['item_size'] = $node->getSize();
$result['item_mtime'] = $node->getMTime();
+ if ($this->trustedServers !== null && in_array($share->getShareType(), [IShare::TYPE_REMOTE, IShare::TYPE_REMOTE_GROUP], true)) {
+ $result['is_trusted_server'] = false;
+ $sharedWith = $share->getSharedWith();
+ $remoteIdentifier = is_string($sharedWith) ? strrchr($sharedWith, '@') : false;
+ if ($remoteIdentifier !== false) {
+ $remote = substr($remoteIdentifier, 1);
+
+ if (isset($this->trustedServerCache[$remote])) {
+ $result['is_trusted_server'] = $this->trustedServerCache[$remote];
+ } else {
+ try {
+ $isTrusted = $this->trustedServers->isTrustedServer($remote);
+ $this->trustedServerCache[$remote] = $isTrusted;
+ $result['is_trusted_server'] = $isTrusted;
+ } catch (\Exception $e) {
+ // Server not found or other issue, we consider it not trusted
+ $this->trustedServerCache[$remote] = false;
+ $this->logger->error(
+ 'Error checking if remote server is trusted (treating as untrusted): ' . $e->getMessage(),
+ ['exception' => $e]
+ );
+ }
+ }
+ }
+ }
+
$expiration = $share->getExpirationDate();
if ($expiration !== null) {
$expiration->setTimezone($this->dateTimeZone->getTimeZone());
@@ -280,7 +317,7 @@ class ShareAPIController extends OCSController {
/** @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) {
+ } catch (ContainerExceptionInterface $e) {
}
} elseif ($share->getShareType() === IShare::TYPE_DECK) {
$result['share_with'] = $share->getSharedWith();
@@ -290,7 +327,7 @@ class ShareAPIController extends OCSController {
/** @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) {
+ } catch (ContainerExceptionInterface $e) {
}
} elseif ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
$result['share_with'] = $share->getSharedWith();
@@ -300,7 +337,7 @@ class ShareAPIController extends OCSController {
/** @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) {
+ } catch (ContainerExceptionInterface $e) {
}
}
@@ -327,7 +364,7 @@ class ShareAPIController extends OCSController {
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], [
+ $result = Server::get(\OCP\Contacts\IManager::class)->search($query, [$property], [
'limit' => 1,
'enumeration' => false,
'strict_search' => true,
@@ -407,7 +444,7 @@ class ShareAPIController extends OCSController {
private 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->isEnabledForAnyone('globalsiteselector')
|| !class_exists('\OCA\GlobalSiteSelector\Service\SlaveService')) {
return [];
}
@@ -470,7 +507,7 @@ class ShareAPIController extends OCSController {
$share = $this->formatShare($share);
if ($include_tags) {
- $share = Helper::populateTags([$share], \OC::$server->getTagManager());
+ $share = $this->populateTags([$share]);
} else {
$share = [$share];
}
@@ -556,6 +593,7 @@ class ShareAPIController extends OCSController {
* 200: Share created
*/
#[NoAdminRequired]
+ #[UserRateLimit(limit: 20, period: 600)]
public function createShare(
?string $path = null,
?int $permissions = null,
@@ -592,7 +630,7 @@ class ShareAPIController extends OCSController {
// combine all permissions to determine if the user can share this file
$nodes = $userFolder->getById($node->getId());
foreach ($nodes as $nodeById) {
- /** @var \OC\Files\FileInfo $fileInfo */
+ /** @var FileInfo $fileInfo */
$fileInfo = $node->getFileInfo();
$fileInfo['permissions'] |= $nodeById->getPermissions();
}
@@ -637,7 +675,9 @@ class ShareAPIController extends OCSController {
$share = $this->setShareAttributes($share, $attributes);
}
- // Expire date
+ // Expire date checks
+ // Normally, null means no expiration date but we still set the default for backwards compatibility
+ // If the client sends an empty string, we set noExpirationDate to true
if ($expireDate !== null) {
if ($expireDate !== '') {
try {
@@ -654,7 +694,6 @@ class ShareAPIController extends OCSController {
}
$share->setSharedBy($this->userId);
- $this->checkInheritedAttributes($share);
// Handle mail send
if (is_null($sendMail)) {
@@ -752,7 +791,7 @@ class ShareAPIController extends OCSController {
$share->setSharedWith($shareWith);
$share->setPermissions($permissions);
} elseif ($shareType === IShare::TYPE_CIRCLE) {
- if (!\OC::$server->getAppManager()->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
+ if (!Server::get(IAppManager::class)->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) {
throw new OCSNotFoundException($this->l->t('You cannot share to a Team if the app is not enabled'));
}
@@ -767,19 +806,19 @@ class ShareAPIController extends OCSController {
} elseif ($shareType === IShare::TYPE_ROOM) {
try {
$this->getRoomShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
- } catch (QueryException $e) {
+ } catch (ContainerExceptionInterface $e) {
throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()]));
}
} elseif ($shareType === IShare::TYPE_DECK) {
try {
$this->getDeckShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
- } catch (QueryException $e) {
+ } catch (ContainerExceptionInterface $e) {
throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support room shares', [$node->getPath()]));
}
} elseif ($shareType === IShare::TYPE_SCIENCEMESH) {
try {
$this->getSciencemeshShareHelper()->createShare($share, $shareWith, $permissions, $expireDate ?? '');
- } catch (QueryException $e) {
+ } catch (ContainerExceptionInterface $e) {
throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not support ScienceMesh shares', [$node->getPath()]));
}
} else {
@@ -787,6 +826,7 @@ class ShareAPIController extends OCSController {
}
$share->setShareType($shareType);
+ $this->checkInheritedAttributes($share);
if ($note !== '') {
$share->setNote($note);
@@ -797,6 +837,9 @@ class ShareAPIController extends OCSController {
} catch (HintException $e) {
$code = $e->getCode() === 0 ? 403 : $e->getCode();
throw new OCSException($e->getHint(), $code);
+ } catch (GenericShareException|\InvalidArgumentException $e) {
+ $this->logger->error($e->getMessage(), ['exception' => $e]);
+ throw new OCSForbiddenException($e->getMessage(), $e);
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
throw new OCSForbiddenException('Failed to create share.', $e);
@@ -839,7 +882,7 @@ class ShareAPIController extends OCSController {
}
if ($includeTags) {
- $formatted = Helper::populateTags($formatted, \OC::$server->getTagManager());
+ $formatted = $this->populateTags($formatted);
}
return $formatted;
@@ -958,9 +1001,9 @@ class ShareAPIController extends OCSController {
: Constants::PERMISSION_READ;
}
- // TODO: It might make sense to have a dedicated setting to allow/deny converting link shares into federated ones
if ($this->hasPermission($permissions, Constants::PERMISSION_READ)
- && $this->shareManager->outgoingServer2ServerSharesAllowed()) {
+ && $this->shareManager->outgoingServer2ServerSharesAllowed()
+ && $this->appConfig->getValueBool('core', ConfigLexicon::SHAREAPI_ALLOW_FEDERATION_ON_PUBLIC_SHARES)) {
$permissions |= Constants::PERMISSION_SHARE;
}
@@ -1092,8 +1135,7 @@ class ShareAPIController extends OCSController {
$formatted = $this->fixMissingDisplayName($formatted);
if ($includeTags) {
- $formatted =
- Helper::populateTags($formatted, \OC::$server->getTagManager());
+ $formatted = $this->populateTags($formatted);
}
return $formatted;
@@ -1250,17 +1292,17 @@ class ShareAPIController extends OCSController {
}
if (
- $permissions === null &&
- $password === null &&
- $sendPasswordByTalk === null &&
- $publicUpload === null &&
- $expireDate === null &&
- $note === null &&
- $label === null &&
- $hideDownload === null &&
- $attributes === null &&
- $sendMail === null &&
- $token === null
+ $permissions === null
+ && $password === null
+ && $sendPasswordByTalk === null
+ && $publicUpload === null
+ && $expireDate === null
+ && $note === null
+ && $label === null
+ && $hideDownload === null
+ && $attributes === null
+ && $sendMail === null
+ && $token === null
) {
throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given'));
}
@@ -1272,7 +1314,6 @@ class ShareAPIController extends OCSController {
if ($attributes !== null) {
$share = $this->setShareAttributes($share, $attributes);
}
- $this->checkInheritedAttributes($share);
// Handle mail send
if ($sendMail === 'true' || $sendMail === 'false') {
@@ -1286,16 +1327,11 @@ class ShareAPIController extends OCSController {
|| $share->getShareType() === IShare::TYPE_EMAIL) {
// Update hide download state
- $attributes = $share->getAttributes() ?? $share->newAttributes();
if ($hideDownload === 'true') {
$share->setHideDownload(true);
- $attributes->setAttribute('permissions', 'download', false);
} elseif ($hideDownload === 'false') {
$share->setHideDownload(false);
- $attributes->setAttribute('permissions', 'download', true);
}
- $share->setAttributes($attributes);
-
// If either manual permissions are specified or publicUpload
// then we need to also update the permissions of the share
@@ -1359,6 +1395,7 @@ class ShareAPIController extends OCSController {
}
try {
+ $this->checkInheritedAttributes($share);
$share = $this->shareManager->updateShare($share);
} catch (HintException $e) {
$code = $e->getCode() === 0 ? 403 : $e->getCode();
@@ -1522,7 +1559,7 @@ class ShareAPIController extends OCSController {
if ($share->getShareType() === IShare::TYPE_ROOM) {
try {
return $this->getRoomShareHelper()->canAccessShare($share, $this->userId);
- } catch (QueryException $e) {
+ } catch (ContainerExceptionInterface $e) {
return false;
}
}
@@ -1530,7 +1567,7 @@ class ShareAPIController extends OCSController {
if ($share->getShareType() === IShare::TYPE_DECK) {
try {
return $this->getDeckShareHelper()->canAccessShare($share, $this->userId);
- } catch (QueryException $e) {
+ } catch (ContainerExceptionInterface $e) {
return false;
}
}
@@ -1538,7 +1575,7 @@ class ShareAPIController extends OCSController {
if ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
try {
return $this->getSciencemeshShareHelper()->canAccessShare($share, $this->userId);
- } catch (QueryException $e) {
+ } catch (ContainerExceptionInterface $e) {
return false;
}
}
@@ -1560,8 +1597,8 @@ class ShareAPIController extends OCSController {
// The owner of the file and the creator of the share
// can always edit the share
- if ($share->getShareOwner() === $this->userId ||
- $share->getSharedBy() === $this->userId
+ if ($share->getShareOwner() === $this->userId
+ || $share->getSharedBy() === $this->userId
) {
return true;
}
@@ -1593,16 +1630,16 @@ class ShareAPIController extends OCSController {
// if the user is the recipient, i can unshare
// the share with self
- if ($share->getShareType() === IShare::TYPE_USER &&
- $share->getSharedWith() === $this->userId
+ if ($share->getShareType() === IShare::TYPE_USER
+ && $share->getSharedWith() === $this->userId
) {
return true;
}
// The owner of the file and the creator of the share
// can always delete the share
- if ($share->getShareOwner() === $this->userId ||
- $share->getSharedBy() === $this->userId
+ if ($share->getShareOwner() === $this->userId
+ || $share->getSharedBy() === $this->userId
) {
return true;
}
@@ -1629,16 +1666,16 @@ class ShareAPIController extends OCSController {
* @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
+ 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->userId ||
- $share->getSharedBy() === $this->userId
+ if ($share->getShareOwner() === $this->userId
+ || $share->getSharedBy() === $this->userId
) {
// Delete the whole share, not just for self
return false;
@@ -1656,7 +1693,7 @@ class ShareAPIController extends OCSController {
if ($share->getShareType() === IShare::TYPE_ROOM) {
try {
return $this->getRoomShareHelper()->canAccessShare($share, $this->userId);
- } catch (QueryException $e) {
+ } catch (ContainerExceptionInterface $e) {
return false;
}
}
@@ -1664,7 +1701,7 @@ class ShareAPIController extends OCSController {
if ($share->getShareType() === IShare::TYPE_DECK) {
try {
return $this->getDeckShareHelper()->canAccessShare($share, $this->userId);
- } catch (QueryException $e) {
+ } catch (ContainerExceptionInterface $e) {
return false;
}
}
@@ -1672,7 +1709,7 @@ class ShareAPIController extends OCSController {
if ($share->getShareType() === IShare::TYPE_SCIENCEMESH) {
try {
return $this->getSciencemeshShareHelper()->canAccessShare($share, $this->userId);
- } catch (QueryException $e) {
+ } catch (ContainerExceptionInterface $e) {
return false;
}
}
@@ -1798,10 +1835,10 @@ 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.
+ * a ContainerExceptionInterface is thrown instead.
*
* @return \OCA\Talk\Share\Helper\ShareAPIController
- * @throws QueryException
+ * @throws ContainerExceptionInterface
*/
private function getRoomShareHelper() {
if (!$this->appManager->isEnabledForUser('spreed')) {
@@ -1815,10 +1852,10 @@ class ShareAPIController extends OCSController {
* 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.
+ * a ContainerExceptionInterface is thrown instead.
*
- * @return \OCA\Deck\Sharing\ShareAPIHelper
- * @throws QueryException
+ * @return ShareAPIHelper
+ * @throws ContainerExceptionInterface
*/
private function getDeckShareHelper() {
if (!$this->appManager->isEnabledForUser('deck')) {
@@ -1832,10 +1869,10 @@ class ShareAPIController extends OCSController {
* 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.
+ * a ContainerExceptionInterface is thrown instead.
*
- * @return \OCA\Deck\Sharing\ShareAPIHelper
- * @throws QueryException
+ * @return ShareAPIHelper
+ * @throws ContainerExceptionInterface
*/
private function getSciencemeshShareHelper() {
if (!$this->appManager->isEnabledForUser('sciencemesh')) {
@@ -1871,8 +1908,8 @@ class ShareAPIController extends OCSController {
continue;
}
- $providerShares =
- $this->shareManager->getSharesBy($viewer, $provider, $node, $reShares, -1, 0);
+ $providerShares
+ = $this->shareManager->getSharesBy($viewer, $provider, $node, $reShares, -1, 0);
$shares = array_merge($shares, $providerShares);
}
@@ -1968,7 +2005,7 @@ class ShareAPIController extends OCSController {
return true;
}
- if ($share->getShareType() === IShare::TYPE_CIRCLE && \OC::$server->getAppManager()->isEnabledForUser('circles')
+ if ($share->getShareType() === IShare::TYPE_CIRCLE && Server::get(IAppManager::class)->isEnabledForUser('circles')
&& class_exists('\OCA\Circles\Api\v1\Circles')) {
$hasCircleId = (str_ends_with($share->getSharedWith(), ']'));
$shareWithStart = ($hasCircleId ? strrpos($share->getSharedWith(), '[') + 1 : 0);
@@ -1984,7 +2021,7 @@ class ShareAPIController extends OCSController {
return true;
}
return false;
- } catch (QueryException $e) {
+ } catch (ContainerExceptionInterface $e) {
return false;
}
}
@@ -2083,30 +2120,50 @@ class ShareAPIController extends OCSController {
if (!$share->getSharedBy()) {
return; // Probably in a test
}
+
+ $canDownload = false;
+ $hideDownload = true;
+
$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
- $node = $userFolder->getFirstNodeById($share->getNodeId());
- if (!$node) {
- return;
- }
- if ($node->getStorage()->instanceOfStorage(SharedStorage::class)) {
- $storage = $node->getStorage();
- if ($storage instanceof Wrapper) {
- $storage = $storage->getInstanceOfStorage(SharedStorage::class);
- if ($storage === null) {
- throw new \RuntimeException('Should not happen, instanceOfStorage but getInstanceOfStorage return null');
- }
- } else {
- throw new \RuntimeException('Should not happen, instanceOfStorage but not a wrapper');
+ $nodes = $userFolder->getById($share->getNodeId());
+ foreach ($nodes as $node) {
+ // Owner always can download it - so allow it and break
+ if ($node->getOwner()?->getUID() === $share->getSharedBy()) {
+ $canDownload = true;
+ $hideDownload = false;
+ break;
}
- /** @var SharedStorage $storage */
- $inheritedAttributes = $storage->getShare()->getAttributes();
- if ($inheritedAttributes !== null && $inheritedAttributes->getAttribute('permissions', 'download') === false) {
- $share->setHideDownload(true);
- $attributes = $share->getAttributes();
- if ($attributes) {
- $attributes->setAttribute('permissions', 'download', false);
- $share->setAttributes($attributes);
+
+ if ($node->getStorage()->instanceOfStorage(SharedStorage::class)) {
+ $storage = $node->getStorage();
+ if ($storage instanceof Wrapper) {
+ $storage = $storage->getInstanceOfStorage(SharedStorage::class);
+ if ($storage === null) {
+ throw new \RuntimeException('Should not happen, instanceOfStorage but getInstanceOfStorage return null');
+ }
+ } else {
+ throw new \RuntimeException('Should not happen, instanceOfStorage but not a wrapper');
}
+
+ /** @var SharedStorage $storage */
+ $originalShare = $storage->getShare();
+ $inheritedAttributes = $originalShare->getAttributes();
+ // hide if hidden and also the current share enforces hide (can only be false if one share is false or user is owner)
+ $hideDownload = $hideDownload && $originalShare->getHideDownload();
+ // allow download if already allowed by previous share or when the current share allows downloading
+ $canDownload = $canDownload || $inheritedAttributes === null || $inheritedAttributes->getAttribute('permissions', 'download') !== false;
+ } elseif ($node->getStorage()->instanceOfStorage(Storage::class)) {
+ $canDownload = true; // in case of federation storage, we can expect the download to be activated by default
+ }
+ }
+
+ if ($hideDownload || !$canDownload) {
+ $share->setHideDownload(true);
+
+ if (!$canDownload) {
+ $attributes = $share->getAttributes() ?? $share->newAttributes();
+ $attributes->setAttribute('permissions', 'download', false);
+ $share->setAttributes($attributes);
}
}
}
@@ -2125,7 +2182,7 @@ class ShareAPIController extends OCSController {
* 200: The email notification was sent successfully
*/
#[NoAdminRequired]
- #[UserRateLimit(limit: 5, period: 120)]
+ #[UserRateLimit(limit: 10, period: 600)]
public function sendShareEmail(string $id, $password = ''): DataResponse {
try {
$share = $this->getShareById($id);
@@ -2198,4 +2255,41 @@ class ShareAPIController extends OCSController {
throw new OCSException($this->l->t('Failed to generate a unique token'));
}
}
+
+ /**
+ * Populate the result set with file tags
+ *
+ * @psalm-template T of array{tags?: list<string>, file_source: int, ...array<string, mixed>}
+ * @param list<T> $fileList
+ * @return list<T> file list populated with tags
+ */
+ private function populateTags(array $fileList): array {
+ $tagger = $this->tagManager->load('files');
+ $tags = $tagger->getTagsForObjects(array_map(static fn (array $fileData) => $fileData['file_source'], $fileList));
+
+ if (!is_array($tags)) {
+ throw new \UnexpectedValueException('$tags must be an array');
+ }
+
+ // Set empty tag array
+ foreach ($fileList as &$fileData) {
+ $fileData['tags'] = [];
+ }
+ unset($fileData);
+
+ if (!empty($tags)) {
+ foreach ($tags as $fileId => $fileTags) {
+ foreach ($fileList as &$fileData) {
+ if ($fileId !== $fileData['file_source']) {
+ continue;
+ }
+
+ $fileData['tags'] = $fileTags;
+ }
+ unset($fileData);
+ }
+ }
+
+ return $fileList;
+ }
}
diff --git a/apps/files_sharing/lib/Controller/ShareController.php b/apps/files_sharing/lib/Controller/ShareController.php
index 1c3c9534dde..5a776379fce 100644
--- a/apps/files_sharing/lib/Controller/ShareController.php
+++ b/apps/files_sharing/lib/Controller/ShareController.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -9,7 +10,6 @@ namespace OCA\Files_Sharing\Controller;
use OC\Security\CSP\ContentSecurityPolicy;
use OCA\DAV\Connector\Sabre\PublicAuth;
use OCA\FederatedFileSharing\FederatedShareProvider;
-use OCA\Files_Sharing\Activity\Providers\Downloads;
use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent;
use OCA\Files_Sharing\Event\ShareLinkAccessedEvent;
use OCP\Accounts\IAccountManager;
@@ -28,7 +28,6 @@ use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
-use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\HintException;
use OCP\IConfig;
@@ -253,9 +252,9 @@ class ShareController extends AuthPublicShareController {
* Emit a ShareLinkAccessedEvent event when a share is accessed, downloaded, auth...
*/
protected function emitShareAccessEvent(IShare $share, string $step = '', int $errorCode = 200, string $errorMessage = ''): void {
- if ($step !== self::SHARE_ACCESS &&
- $step !== self::SHARE_AUTH &&
- $step !== self::SHARE_DOWNLOAD) {
+ if ($step !== self::SHARE_ACCESS
+ && $step !== self::SHARE_AUTH
+ && $step !== self::SHARE_DOWNLOAD) {
return;
}
$this->eventDispatcher->dispatchTyped(new ShareLinkAccessedEvent($share, $step, $errorCode, $errorMessage));
@@ -359,19 +358,18 @@ class ShareController extends AuthPublicShareController {
return new DataResponse('Share has no read permission');
}
+ $attributes = $share->getAttributes();
+ if ($attributes?->getAttribute('permissions', 'download') === false) {
+ return new DataResponse('Share has no download permission');
+ }
+
if (!$this->validateShare($share)) {
throw new NotFoundException();
}
- // Single file share
- if ($share->getNode() instanceof File) {
- // Single file download
- $this->singleFileDownloaded($share, $share->getNode());
- }
- // Directory share
- else {
- /** @var Folder $node */
- $node = $share->getNode();
+ $node = $share->getNode();
+ if ($node instanceof Folder) {
+ // Directory share
// Try to get the path
if ($path !== '') {
@@ -386,22 +384,10 @@ class ShareController extends AuthPublicShareController {
if ($node instanceof Folder) {
if ($files === null || $files === '') {
- // The folder is downloaded
- $this->singleFileDownloaded($share, $share->getNode());
- } else {
- $fileList = json_decode($files);
- // in case we get only a single file
- if (!is_array($fileList)) {
- $fileList = [$fileList];
- }
- foreach ($fileList as $file) {
- $subNode = $node->get($file);
- $this->singleFileDownloaded($share, $subNode);
+ if ($share->getHideDownload()) {
+ throw new NotFoundException('Downloading a folder');
}
}
- } else {
- // Single file download
- $this->singleFileDownloaded($share, $share->getNode());
}
}
@@ -414,77 +400,4 @@ class ShareController extends AuthPublicShareController {
}
return new RedirectResponse($this->urlGenerator->getAbsoluteURL($davUrl));
}
-
- /**
- * create activity if a single file was downloaded from a link share
- *
- * @param Share\IShare $share
- * @throws NotFoundException when trying to download a folder of a "hide download" share
- */
- protected function singleFileDownloaded(IShare $share, Node $node) {
- if ($share->getHideDownload() && $node instanceof Folder) {
- throw new NotFoundException('Downloading a folder');
- }
-
- $fileId = $node->getId();
-
- $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
- $userNode = $userFolder->getFirstNodeById($fileId);
- $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
- $userPath = $userFolder->getRelativePath($userNode->getPath());
- $ownerPath = $ownerFolder->getRelativePath($node->getPath());
- $remoteAddress = $this->request->getRemoteAddress();
- $dateTime = new \DateTime();
- $dateTime = $dateTime->format('Y-m-d H');
- $remoteAddressHash = md5($dateTime . '-' . $remoteAddress);
-
- $parameters = [$userPath];
-
- if ($share->getShareType() === IShare::TYPE_EMAIL) {
- if ($node instanceof File) {
- $subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
- } else {
- $subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
- }
- $parameters[] = $share->getSharedWith();
- } else {
- if ($node instanceof File) {
- $subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
- $parameters[] = $remoteAddressHash;
- } else {
- $subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
- $parameters[] = $remoteAddressHash;
- }
- }
-
- $this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
-
- if ($share->getShareOwner() !== $share->getSharedBy()) {
- $parameters[0] = $ownerPath;
- $this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
- }
- }
-
- /**
- * publish activity
- *
- * @param string $subject
- * @param array $parameters
- * @param string $affectedUser
- * @param int $fileId
- * @param string $filePath
- */
- protected function publishActivity($subject,
- array $parameters,
- $affectedUser,
- $fileId,
- $filePath) {
- $event = $this->activityManager->generateEvent();
- $event->setApp('files_sharing')
- ->setType('public_links')
- ->setSubject($subject, $parameters)
- ->setAffectedUser($affectedUser)
- ->setObject('files', $fileId, $filePath);
- $this->activityManager->publish($event);
- }
}
diff --git a/apps/files_sharing/lib/Controller/ShareInfoController.php b/apps/files_sharing/lib/Controller/ShareInfoController.php
index 0a290d69c65..b7e79aec830 100644
--- a/apps/files_sharing/lib/Controller/ShareInfoController.php
+++ b/apps/files_sharing/lib/Controller/ShareInfoController.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/Controller/ShareesAPIController.php b/apps/files_sharing/lib/Controller/ShareesAPIController.php
index 9a9e94a7ee2..0c458ce9662 100644
--- a/apps/files_sharing/lib/Controller/ShareesAPIController.php
+++ b/apps/files_sharing/lib/Controller/ShareesAPIController.php
@@ -12,6 +12,7 @@ use Generator;
use OC\Collaboration\Collaborators\SearchResult;
use OC\Share\Share;
use OCA\Files_Sharing\ResponseDefinitions;
+use OCP\App\IAppManager;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
@@ -21,9 +22,11 @@ use OCP\Collaboration\Collaborators\ISearch;
use OCP\Collaboration\Collaborators\ISearchResult;
use OCP\Collaboration\Collaborators\SearchResultType;
use OCP\Constants;
+use OCP\GlobalScale\IConfig as GlobalScaleIConfig;
use OCP\IConfig;
use OCP\IRequest;
use OCP\IURLGenerator;
+use OCP\Server;
use OCP\Share\IManager;
use OCP\Share\IShare;
use function array_slice;
@@ -155,7 +158,7 @@ class ShareesAPIController extends OCSController {
}
// FIXME: DI
- if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) {
+ if (Server::get(IAppManager::class)->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) {
$shareTypes[] = IShare::TYPE_CIRCLE;
}
@@ -173,15 +176,11 @@ class ShareesAPIController extends OCSController {
$this->limit = $perPage;
$this->offset = $perPage * ($page - 1);
- // In global scale mode we always search the loogup server
- if ($this->config->getSystemValueBool('gs.enabled', false)) {
- $lookup = true;
- $this->result['lookupEnabled'] = true;
- } else {
- $this->result['lookupEnabled'] = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'yes') === 'yes';
- }
+ // In global scale mode we always search the lookup server
+ $this->result['lookupEnabled'] = Server::get(GlobalScaleIConfig::class)->isGlobalScaleEnabled();
+ // TODO: Reconsider using lookup server for non-global-scale federation
- [$result, $hasMoreResults] = $this->collaboratorSearch->search($search, $shareTypes, $lookup, $this->limit, $this->offset);
+ [$result, $hasMoreResults] = $this->collaboratorSearch->search($search, $shareTypes, $this->result['lookupEnabled'], $this->limit, $this->offset);
// extra treatment for 'exact' subarray, with a single merge expected keys might be lost
if (isset($result['exact'])) {
@@ -328,7 +327,7 @@ class ShareesAPIController extends OCSController {
}
// FIXME: DI
- if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) {
+ if (Server::get(IAppManager::class)->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) {
$shareTypes[] = IShare::TYPE_CIRCLE;
}
diff --git a/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php b/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php
index 0c76f0e1742..afba45cac4a 100644
--- a/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php
+++ b/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php
@@ -70,8 +70,10 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider
$ownerNameProperty = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
if ($ownerNameProperty->getScope() === IAccountManager::SCOPE_PUBLISHED) {
- $ownerName = $owner->getDisplayName();
$ownerId = $owner->getUID();
+ $ownerName = $owner->getDisplayName();
+ $this->initialState->provideInitialState('owner', $ownerId);
+ $this->initialState->provideInitialState('ownerDisplayName', $ownerName);
}
}
@@ -89,6 +91,9 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider
'disclaimer',
$this->appConfig->getValueString('core', 'shareapi_public_link_disclaimertext'),
);
+ // file drops do not request the root folder so we need to provide label and note if available
+ $this->initialState->provideInitialState('label', $share->getLabel());
+ $this->initialState->provideInitialState('note', $share->getNote());
}
// Set up initial state
$this->initialState->provideInitialState('isPublic', true);
@@ -102,13 +107,12 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider
Util::addInitScript(Application::APP_ID, 'init');
Util::addInitScript(Application::APP_ID, 'init-public');
Util::addScript('files', 'main');
+ Util::addScript(Application::APP_ID, 'public-nickname-handler');
// Add file-request script if needed
$attributes = $share->getAttributes();
$isFileRequest = $attributes?->getAttribute('fileRequest', 'enabled') === true;
- if ($isFileRequest) {
- Util::addScript(Application::APP_ID, 'public-file-request');
- }
+ $this->initialState->provideInitialState('isFileRequest', $isFileRequest);
// Load Viewer scripts
if (class_exists(LoadViewer::class)) {
@@ -118,8 +122,7 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider
// Allow external apps to register their scripts
$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($share));
- // OpenGraph Support: http://ogp.me/
- $this->addOpenGraphHeaders($share);
+ $this->addMetaHeaders($share);
// CSP to allow office
$csp = new ContentSecurityPolicy();
@@ -146,18 +149,15 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider
// Create the header action menu
$headerActions = [];
- if ($view !== 'public-file-drop') {
+ if ($view !== 'public-file-drop' && !$share->getHideDownload()) {
// The download URL is used for the "download" header action as well as in some cases for the direct link
- $downloadUrl = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', [
- 'token' => $token,
- 'filename' => ($shareNode instanceof File) ? $shareNode->getName() : null,
- ]);
+ $downloadUrl = $this->urlGenerator->getAbsoluteURL('/public.php/dav/files/' . $token . '/?accept=zip');
// If not a file drop, then add the download header action
$headerActions[] = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $downloadUrl, 0, (string)$shareNode->getSize());
// If remote sharing is enabled also add the remote share action to the menu
- if ($this->federatedShareProvider->isOutgoingServer2serverShareEnabled() && !$share->getHideDownload()) {
+ if ($this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
$headerActions[] = new ExternalShareMenuAction(
// TRANSLATORS The placeholder refers to the software product name as in 'Add to your Nextcloud'
$this->l10n->t('Add to your %s', [$this->defaults->getProductName()]),
@@ -190,15 +190,17 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider
* Add OpenGraph headers to response for preview
* @param IShare $share The share for which to add the headers
*/
- protected function addOpenGraphHeaders(IShare $share): void {
+ protected function addMetaHeaders(IShare $share): void {
$shareNode = $share->getNode();
$token = $share->getToken();
$shareUrl = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $token]);
// Handle preview generation for OpenGraph
+ $hasImagePreview = false;
if ($this->previewManager->isMimeSupported($shareNode->getMimetype())) {
// For images we can use direct links
if ($shareNode->getMimePart() === 'image') {
+ $hasImagePreview = true;
$ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $token]);
// Whatsapp is kind of picky about their size requirements
if ($this->request->isUserAgent(['/^WhatsApp/'])) {
@@ -226,11 +228,34 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider
$ogPreview = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
}
- Util::addHeader('meta', ['property' => 'og:title', 'content' => $shareNode->getName()]);
- Util::addHeader('meta', ['property' => 'og:description', 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
- Util::addHeader('meta', ['property' => 'og:site_name', 'content' => $this->defaults->getName()]);
+ $title = $shareNode->getName();
+ $siteName = $this->defaults->getName();
+ $description = $siteName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '');
+
+ // OpenGraph Support: http://ogp.me/
+ Util::addHeader('meta', ['property' => 'og:title', 'content' => $title]);
+ Util::addHeader('meta', ['property' => 'og:description', 'content' => $description]);
+ Util::addHeader('meta', ['property' => 'og:site_name', 'content' => $siteName]);
Util::addHeader('meta', ['property' => 'og:url', 'content' => $shareUrl]);
- Util::addHeader('meta', ['property' => 'og:type', 'content' => 'object']);
- Util::addHeader('meta', ['property' => 'og:image', 'content' => $ogPreview]);
+ Util::addHeader('meta', ['property' => 'og:type', 'content' => 'website']);
+ Util::addHeader('meta', ['property' => 'og:image', 'content' => $ogPreview]); // recommended to always have the image
+ if ($shareNode->getMimePart() === 'image') {
+ Util::addHeader('meta', ['property' => 'og:image:type', 'content' => $shareNode->getMimeType()]);
+ } elseif ($shareNode->getMimePart() === 'audio') {
+ $audio = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadshare', ['token' => $token]);
+ Util::addHeader('meta', ['property' => 'og:audio', 'content' => $audio]);
+ Util::addHeader('meta', ['property' => 'og:audio:type', 'content' => $shareNode->getMimeType()]);
+ } elseif ($shareNode->getMimePart() === 'video') {
+ $video = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadshare', ['token' => $token]);
+ Util::addHeader('meta', ['property' => 'og:video', 'content' => $video]);
+ Util::addHeader('meta', ['property' => 'og:video:type', 'content' => $shareNode->getMimeType()]);
+ }
+
+
+ // Twitter Support: https://developer.x.com/en/docs/x-for-websites/cards/overview/markup
+ Util::addHeader('meta', ['property' => 'twitter:title', 'content' => $title]);
+ Util::addHeader('meta', ['property' => 'twitter:description', 'content' => $description]);
+ Util::addHeader('meta', ['property' => 'twitter:card', 'content' => $hasImagePreview ? 'summary_large_image' : 'summary']);
+ Util::addHeader('meta', ['property' => 'twitter:image', 'content' => $ogPreview]);
}
}
diff --git a/apps/files_sharing/lib/Exceptions/BrokenPath.php b/apps/files_sharing/lib/Exceptions/BrokenPath.php
index 11b3599d609..a68a8fc05d4 100644
--- a/apps/files_sharing/lib/Exceptions/BrokenPath.php
+++ b/apps/files_sharing/lib/Exceptions/BrokenPath.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2020-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/apps/files_sharing/lib/Exceptions/S2SException.php b/apps/files_sharing/lib/Exceptions/S2SException.php
index fb60b20774a..10360820432 100644
--- a/apps/files_sharing/lib/Exceptions/S2SException.php
+++ b/apps/files_sharing/lib/Exceptions/S2SException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
diff --git a/apps/files_sharing/lib/Exceptions/SharingRightsException.php b/apps/files_sharing/lib/Exceptions/SharingRightsException.php
index 548873cfbfd..2ffe72c4e69 100644
--- a/apps/files_sharing/lib/Exceptions/SharingRightsException.php
+++ b/apps/files_sharing/lib/Exceptions/SharingRightsException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/ExpireSharesJob.php b/apps/files_sharing/lib/ExpireSharesJob.php
index 8ea6fee8a5c..b1c6c592e80 100644
--- a/apps/files_sharing/lib/ExpireSharesJob.php
+++ b/apps/files_sharing/lib/ExpireSharesJob.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -8,6 +9,7 @@ namespace OCA\Files_Sharing;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
+use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
@@ -49,15 +51,9 @@ class ExpireSharesJob extends TimedJob {
->from('share')
->where(
$qb->expr()->andX(
- $qb->expr()->orX(
- $qb->expr()->eq('share_type', $qb->expr()->literal(IShare::TYPE_LINK)),
- $qb->expr()->eq('share_type', $qb->expr()->literal(IShare::TYPE_EMAIL))
- ),
+ $qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_LINK, IShare::TYPE_EMAIL], IQueryBuilder::PARAM_INT_ARRAY)),
$qb->expr()->lte('expiration', $qb->expr()->literal($now)),
- $qb->expr()->orX(
- $qb->expr()->eq('item_type', $qb->expr()->literal('file')),
- $qb->expr()->eq('item_type', $qb->expr()->literal('folder'))
- )
+ $qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))
)
);
diff --git a/apps/files_sharing/lib/External/Cache.php b/apps/files_sharing/lib/External/Cache.php
index 3bf6d09e681..027f682d818 100644
--- a/apps/files_sharing/lib/External/Cache.php
+++ b/apps/files_sharing/lib/External/Cache.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/apps/files_sharing/lib/External/Manager.php b/apps/files_sharing/lib/External/Manager.php
index ff7f2dd3c48..ff4781eba0f 100644
--- a/apps/files_sharing/lib/External/Manager.php
+++ b/apps/files_sharing/lib/External/Manager.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -40,12 +41,9 @@ class Manager {
/** @var string|null */
private $uid;
- /** @var \OC\Files\Mount\Manager */
- private $mountManager;
-
public function __construct(
private IDBConnection $connection,
- \OC\Files\Mount\Manager $mountManager,
+ private \OC\Files\Mount\Manager $mountManager,
private IStorageFactory $storageLoader,
private IClientService $clientService,
private IManager $notificationManager,
@@ -59,7 +57,6 @@ class Manager {
private LoggerInterface $logger,
) {
$user = $userSession->getUser();
- $this->mountManager = $mountManager;
$this->uid = $user ? $user->getUID() : null;
}
diff --git a/apps/files_sharing/lib/External/Mount.php b/apps/files_sharing/lib/External/Mount.php
index d7ab61e0364..f50c379f85f 100644
--- a/apps/files_sharing/lib/External/Mount.php
+++ b/apps/files_sharing/lib/External/Mount.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/apps/files_sharing/lib/External/MountProvider.php b/apps/files_sharing/lib/External/MountProvider.php
index a29fdd2d544..a5781d5d35a 100644
--- a/apps/files_sharing/lib/External/MountProvider.php
+++ b/apps/files_sharing/lib/External/MountProvider.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -10,8 +11,10 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Federation\ICloudIdManager;
use OCP\Files\Config\IMountProvider;
use OCP\Files\Storage\IStorageFactory;
+use OCP\Http\Client\IClientService;
use OCP\IDBConnection;
use OCP\IUser;
+use OCP\Server;
class MountProvider implements IMountProvider {
public const STORAGE = '\OCA\Files_Sharing\External\Storage';
@@ -42,7 +45,7 @@ class MountProvider implements IMountProvider {
$data['mountpoint'] = $mountPoint;
$data['cloudId'] = $this->cloudIdManager->getCloudId($data['owner'], $data['remote']);
$data['certificateManager'] = \OC::$server->getCertificateManager();
- $data['HttpClientService'] = \OC::$server->getHTTPClientService();
+ $data['HttpClientService'] = Server::get(IClientService::class);
return new Mount(self::STORAGE, $mountPoint, $data, $manager, $storageFactory);
}
diff --git a/apps/files_sharing/lib/External/Scanner.php b/apps/files_sharing/lib/External/Scanner.php
index e45d3723923..0d57248595b 100644
--- a/apps/files_sharing/lib/External/Scanner.php
+++ b/apps/files_sharing/lib/External/Scanner.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -6,6 +7,7 @@
*/
namespace OCA\Files_Sharing\External;
+use OC\Files\Cache\CacheEntry;
use OC\ForbiddenException;
use OCP\Files\NotFoundException;
use OCP\Files\StorageInvalidException;
@@ -29,7 +31,7 @@ class Scanner extends \OC\Files\Cache\Scanner {
* @param string $file file to scan
* @param int $reuseExisting
* @param int $parentId
- * @param \OC\Files\Cache\CacheEntry|array|null|false $cacheData existing data in the cache for the file to be scanned
+ * @param CacheEntry|array|null|false $cacheData existing data in the cache for the file to be scanned
* @param bool $lock set to false to disable getting an additional read lock during scanning
* @param array|null $data the metadata for the file, as returned by the storage
* @return array|null an array of metadata of the scanned file
diff --git a/apps/files_sharing/lib/External/Storage.php b/apps/files_sharing/lib/External/Storage.php
index bacd2b3f7cf..a9781b91a6c 100644
--- a/apps/files_sharing/lib/External/Storage.php
+++ b/apps/files_sharing/lib/External/Storage.php
@@ -53,7 +53,7 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
* @param array{HttpClientService: IClientService, manager: ExternalShareManager, cloudId: ICloudId, mountpoint: string, token: string, password: ?string}|array $options
*/
public function __construct($options) {
- $this->memcacheFactory = \OC::$server->getMemCacheFactory();
+ $this->memcacheFactory = Server::get(ICacheFactory::class);
$this->httpClient = $options['HttpClientService'];
$this->manager = $options['manager'];
$this->cloudId = $options['cloudId'];
@@ -304,7 +304,7 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
$url = rtrim($remote, '/') . '/index.php/apps/files_sharing/shareinfo?t=' . $token;
// TODO: DI
- $client = \OC::$server->getHTTPClientService()->newClient();
+ $client = Server::get(IClientService::class)->newClient();
try {
$response = $client->post($url, array_merge($this->getDefaultRequestOptions(), [
'body' => ['password' => $password, 'depth' => $depth],
diff --git a/apps/files_sharing/lib/External/Watcher.php b/apps/files_sharing/lib/External/Watcher.php
index ccd6bd179b3..f3616feabba 100644
--- a/apps/files_sharing/lib/External/Watcher.php
+++ b/apps/files_sharing/lib/External/Watcher.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/apps/files_sharing/lib/Helper.php b/apps/files_sharing/lib/Helper.php
index 1e5c5c8abe5..92e874b73db 100644
--- a/apps/files_sharing/lib/Helper.php
+++ b/apps/files_sharing/lib/Helper.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -9,6 +10,8 @@ namespace OCA\Files_Sharing;
use OC\Files\Filesystem;
use OC\Files\View;
use OCA\Files_Sharing\AppInfo\Application;
+use OCP\IConfig;
+use OCP\Server;
use OCP\Util;
class Helper {
@@ -52,7 +55,7 @@ class Helper {
$view = Filesystem::getView();
}
- $config = \OC::$server->getConfig();
+ $config = Server::get(IConfig::class);
$systemDefault = $config->getSystemValue('share_folder', '/');
$allowCustomShareFolder = $config->getSystemValueBool('sharing.allow_custom_share_folder', true);
@@ -86,6 +89,6 @@ class Helper {
* @param string $shareFolder
*/
public static function setShareFolder($shareFolder) {
- \OC::$server->getConfig()->setSystemValue('share_folder', $shareFolder);
+ Server::get(IConfig::class)->setSystemValue('share_folder', $shareFolder);
}
}
diff --git a/apps/files_sharing/lib/Hooks.php b/apps/files_sharing/lib/Hooks.php
index 6a297f1e875..e90b9f5c23d 100644
--- a/apps/files_sharing/lib/Hooks.php
+++ b/apps/files_sharing/lib/Hooks.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -8,10 +9,11 @@ namespace OCA\Files_Sharing;
use OC\Files\Filesystem;
use OC\Files\View;
+use OCP\Server;
class Hooks {
public static function deleteUser($params) {
- $manager = \OC::$server->get(External\Manager::class);
+ $manager = Server::get(External\Manager::class);
$manager->removeUserShares($params['uid']);
}
diff --git a/apps/files_sharing/lib/ISharedStorage.php b/apps/files_sharing/lib/ISharedStorage.php
index dfd8b134db1..9bd3e4c9476 100644
--- a/apps/files_sharing/lib/ISharedStorage.php
+++ b/apps/files_sharing/lib/ISharedStorage.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/apps/files_sharing/lib/Listener/BeforeNodeReadListener.php b/apps/files_sharing/lib/Listener/BeforeNodeReadListener.php
new file mode 100644
index 00000000000..d19bc8dfae9
--- /dev/null
+++ b/apps/files_sharing/lib/Listener/BeforeNodeReadListener.php
@@ -0,0 +1,189 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Files_Sharing\Listener;
+
+use OCA\Files_Sharing\Activity\Providers\Downloads;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\Files\Events\BeforeZipCreatedEvent;
+use OCP\Files\Events\Node\BeforeNodeReadEvent;
+use OCP\Files\File;
+use OCP\Files\Folder;
+use OCP\Files\IRootFolder;
+use OCP\Files\NotFoundException;
+use OCP\Files\Storage\ISharedStorage;
+use OCP\ICache;
+use OCP\ICacheFactory;
+use OCP\IRequest;
+use OCP\ISession;
+use OCP\Share\IShare;
+
+/**
+ * @template-implements IEventListener<BeforeNodeReadEvent|BeforeZipCreatedEvent|Event>
+ */
+class BeforeNodeReadListener implements IEventListener {
+ private ICache $cache;
+
+ public function __construct(
+ private ISession $session,
+ private IRootFolder $rootFolder,
+ private \OCP\Activity\IManager $activityManager,
+ private IRequest $request,
+ ICacheFactory $cacheFactory,
+ ) {
+ $this->cache = $cacheFactory->createDistributed('files_sharing_activity_events');
+ }
+
+ public function handle(Event $event): void {
+ if ($event instanceof BeforeZipCreatedEvent) {
+ $this->handleBeforeZipCreatedEvent($event);
+ } elseif ($event instanceof BeforeNodeReadEvent) {
+ $this->handleBeforeNodeReadEvent($event);
+ }
+ }
+
+ public function handleBeforeZipCreatedEvent(BeforeZipCreatedEvent $event): void {
+ $files = $event->getFiles();
+ if (count($files) !== 0) {
+ /* No need to do anything, activity will be triggered for each file in the zip by the BeforeNodeReadEvent */
+ return;
+ }
+
+ $node = $event->getFolder();
+ if (!($node instanceof Folder)) {
+ return;
+ }
+
+ try {
+ $storage = $node->getStorage();
+ } catch (NotFoundException) {
+ return;
+ }
+
+ if (!$storage->instanceOfStorage(ISharedStorage::class)) {
+ return;
+ }
+
+ /** @var ISharedStorage $storage */
+ $share = $storage->getShare();
+
+ if (!in_array($share->getShareType(), [IShare::TYPE_EMAIL, IShare::TYPE_LINK])) {
+ return;
+ }
+
+ /* Cache that that folder download activity was published */
+ $this->cache->set($this->request->getId(), $node->getPath(), 3600);
+
+ $this->singleFileDownloaded($share, $node);
+ }
+
+ public function handleBeforeNodeReadEvent(BeforeNodeReadEvent $event): void {
+ $node = $event->getNode();
+ if (!($node instanceof File)) {
+ return;
+ }
+
+ try {
+ $storage = $node->getStorage();
+ } catch (NotFoundException) {
+ return;
+ }
+
+ if (!$storage->instanceOfStorage(ISharedStorage::class)) {
+ return;
+ }
+
+ /** @var ISharedStorage $storage */
+ $share = $storage->getShare();
+
+ if (!in_array($share->getShareType(), [IShare::TYPE_EMAIL, IShare::TYPE_LINK])) {
+ return;
+ }
+
+ $path = $this->cache->get($this->request->getId());
+ if (is_string($path) && str_starts_with($node->getPath(), $path)) {
+ /* An activity was published for a containing folder already */
+ return;
+ }
+
+ /* Avoid publishing several activities for one video playing */
+ $cacheKey = $node->getId() . $node->getPath() . $this->session->getId();
+ if (($this->request->getHeader('range') !== '') && ($this->cache->get($cacheKey) === 'true')) {
+ /* This is a range request and an activity for the same file was published in the same session */
+ return;
+ }
+ $this->cache->set($cacheKey, 'true', 3600);
+
+ $this->singleFileDownloaded($share, $node);
+ }
+
+ /**
+ * create activity if a single file or folder was downloaded from a link share
+ */
+ protected function singleFileDownloaded(IShare $share, File|Folder $node): void {
+ $fileId = $node->getId();
+
+ $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
+ $userNode = $userFolder->getFirstNodeById($fileId);
+ $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
+ $userPath = $userFolder->getRelativePath($userNode?->getPath() ?? '') ?? '';
+ $ownerPath = $ownerFolder->getRelativePath($node->getPath()) ?? '';
+
+ $parameters = [$userPath];
+
+ if ($share->getShareType() === IShare::TYPE_EMAIL) {
+ if ($node instanceof File) {
+ $subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
+ } else {
+ $subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
+ }
+ $parameters[] = $share->getSharedWith();
+ } elseif ($share->getShareType() === IShare::TYPE_LINK) {
+ if ($node instanceof File) {
+ $subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
+ } else {
+ $subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
+ }
+ $remoteAddress = $this->request->getRemoteAddress();
+ $dateTime = new \DateTime();
+ $dateTime = $dateTime->format('Y-m-d H');
+ $remoteAddressHash = md5($dateTime . '-' . $remoteAddress);
+ $parameters[] = $remoteAddressHash;
+ } else {
+ return;
+ }
+
+ $this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
+
+ if ($share->getShareOwner() !== $share->getSharedBy()) {
+ $parameters[0] = $ownerPath;
+ $this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
+ }
+ }
+
+ /**
+ * publish activity
+ */
+ protected function publishActivity(
+ string $subject,
+ array $parameters,
+ string $affectedUser,
+ int $fileId,
+ string $filePath,
+ ): void {
+ $event = $this->activityManager->generateEvent();
+ $event->setApp('files_sharing')
+ ->setType('public_links')
+ ->setSubject($subject, $parameters)
+ ->setAffectedUser($affectedUser)
+ ->setObject('files', $fileId, $filePath);
+ $this->activityManager->publish($event);
+ }
+}
diff --git a/apps/files_sharing/lib/Listener/LoadAdditionalListener.php b/apps/files_sharing/lib/Listener/LoadAdditionalListener.php
index fc6516a83e2..b089c8309b7 100644
--- a/apps/files_sharing/lib/Listener/LoadAdditionalListener.php
+++ b/apps/files_sharing/lib/Listener/LoadAdditionalListener.php
@@ -12,6 +12,7 @@ use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCA\Files_Sharing\AppInfo\Application;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
+use OCP\Server;
use OCP\Share\IManager;
use OCP\Util;
@@ -26,7 +27,7 @@ class LoadAdditionalListener implements IEventListener {
Util::addScript(Application::APP_ID, 'additionalScripts', 'files');
Util::addStyle(Application::APP_ID, 'icons');
- $shareManager = \OC::$server->get(IManager::class);
+ $shareManager = Server::get(IManager::class);
if ($shareManager->shareApiEnabled() && class_exists('\OCA\Files\App')) {
Util::addInitScript(Application::APP_ID, 'init');
}
diff --git a/apps/files_sharing/lib/Listener/LoadPublicFileRequestAuthListener.php b/apps/files_sharing/lib/Listener/LoadPublicFileRequestAuthListener.php
index f1e054c7ee5..6da2476194b 100644
--- a/apps/files_sharing/lib/Listener/LoadPublicFileRequestAuthListener.php
+++ b/apps/files_sharing/lib/Listener/LoadPublicFileRequestAuthListener.php
@@ -10,6 +10,7 @@ namespace OCA\Files_Sharing\Listener;
use OCA\Files_Sharing\AppInfo\Application;
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Share\IManager;
@@ -19,6 +20,7 @@ use OCP\Util;
class LoadPublicFileRequestAuthListener implements IEventListener {
public function __construct(
private IManager $shareManager,
+ private IInitialState $initialState,
) {
}
@@ -51,9 +53,9 @@ class LoadPublicFileRequestAuthListener implements IEventListener {
// Ignore, this is not a file request or the share does not exist
}
- if ($isFileRequest) {
- // Add the script to the public page
- Util::addScript(Application::APP_ID, 'public-file-request');
- }
+ Util::addScript(Application::APP_ID, 'public-nickname-handler');
+
+ // Add file-request script if needed
+ $this->initialState->provideInitialState('isFileRequest', $isFileRequest);
}
}
diff --git a/apps/files_sharing/lib/Listener/LoadSidebarListener.php b/apps/files_sharing/lib/Listener/LoadSidebarListener.php
index b00e937d675..17fee71978f 100644
--- a/apps/files_sharing/lib/Listener/LoadSidebarListener.php
+++ b/apps/files_sharing/lib/Listener/LoadSidebarListener.php
@@ -11,9 +11,13 @@ namespace OCA\Files_Sharing\Listener;
use OCA\Files\Event\LoadSidebar;
use OCA\Files_Sharing\AppInfo\Application;
+use OCA\Files_Sharing\Config\ConfigLexicon;
use OCP\AppFramework\Services\IInitialState;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
+use OCP\GlobalScale\IConfig;
+use OCP\IAppConfig;
+use OCP\Server;
use OCP\Share\IManager;
use OCP\Util;
@@ -32,7 +36,15 @@ class LoadSidebarListener implements IEventListener {
if (!($event instanceof LoadSidebar)) {
return;
}
-
Util::addScript(Application::APP_ID, 'files_sharing_tab', 'files');
+
+ $appConfig = Server::get(IAppConfig::class);
+ $gsConfig = Server::get(IConfig::class);
+ $showFederatedToTrustedAsInternal = $gsConfig->isGlobalScaleEnabled() || $appConfig->getValueBool('files_sharing', ConfigLexicon::SHOW_FEDERATED_TO_TRUSTED_AS_INTERNAL);
+ $showFederatedAsInternal = ($gsConfig->isGlobalScaleEnabled() && $gsConfig->onlyInternalFederation())
+ || $appConfig->getValueBool('files_sharing', ConfigLexicon::SHOW_FEDERATED_AS_INTERNAL);
+
+ $this->initialState->provideInitialState('showFederatedSharesAsInternal', $showFederatedAsInternal);
+ $this->initialState->provideInitialState('showFederatedSharesToTrustedServersAsInternal', $showFederatedToTrustedAsInternal);
}
}
diff --git a/apps/files_sharing/lib/Middleware/OCSShareAPIMiddleware.php b/apps/files_sharing/lib/Middleware/OCSShareAPIMiddleware.php
index ad34a1d2ab9..6671a78efff 100644
--- a/apps/files_sharing/lib/Middleware/OCSShareAPIMiddleware.php
+++ b/apps/files_sharing/lib/Middleware/OCSShareAPIMiddleware.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/Middleware/ShareInfoMiddleware.php b/apps/files_sharing/lib/Middleware/ShareInfoMiddleware.php
index 1f29e855eb5..e96940979bf 100644
--- a/apps/files_sharing/lib/Middleware/ShareInfoMiddleware.php
+++ b/apps/files_sharing/lib/Middleware/ShareInfoMiddleware.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php b/apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php
index 75ee8d3fb83..8ea2eb59d73 100644
--- a/apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php
+++ b/apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php
@@ -52,8 +52,8 @@ class SharingCheckMiddleware extends Middleware {
throw new NotFoundException('Sharing is disabled.');
}
- if ($controller instanceof ExternalSharesController &&
- !$this->externalSharesChecks()) {
+ if ($controller instanceof ExternalSharesController
+ && !$this->externalSharesChecks()) {
throw new S2SException('Federated sharing not allowed');
}
}
@@ -84,13 +84,13 @@ class SharingCheckMiddleware extends Middleware {
* @return bool
*/
private function externalSharesChecks(): bool {
- if (!$this->reflector->hasAnnotation('NoIncomingFederatedSharingRequired') &&
- $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') !== 'yes') {
+ if (!$this->reflector->hasAnnotation('NoIncomingFederatedSharingRequired')
+ && $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes') !== 'yes') {
return false;
}
- if (!$this->reflector->hasAnnotation('NoOutgoingFederatedSharingRequired') &&
- $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') !== 'yes') {
+ if (!$this->reflector->hasAnnotation('NoOutgoingFederatedSharingRequired')
+ && $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') !== 'yes') {
return false;
}
diff --git a/apps/files_sharing/lib/Migration/OwncloudGuestShareType.php b/apps/files_sharing/lib/Migration/OwncloudGuestShareType.php
index c9511eaa136..3718306e380 100644
--- a/apps/files_sharing/lib/Migration/OwncloudGuestShareType.php
+++ b/apps/files_sharing/lib/Migration/OwncloudGuestShareType.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -51,7 +52,7 @@ class OwncloudGuestShareType implements IRepairStep {
protected function shouldRun() {
$appVersion = $this->config->getAppValue('files_sharing', 'installed_version', '0.0.0');
- return $appVersion === '0.10.0' ||
- $this->config->getAppValue('core', 'vendor', '') === 'owncloud';
+ return $appVersion === '0.10.0'
+ || $this->config->getAppValue('core', 'vendor', '') === 'owncloud';
}
}
diff --git a/apps/files_sharing/lib/Migration/SetPasswordColumn.php b/apps/files_sharing/lib/Migration/SetPasswordColumn.php
index 647e3bef8d3..f60af2817d4 100644
--- a/apps/files_sharing/lib/Migration/SetPasswordColumn.php
+++ b/apps/files_sharing/lib/Migration/SetPasswordColumn.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files_sharing/lib/MountProvider.php b/apps/files_sharing/lib/MountProvider.php
index ad9498371d3..7a0b1f135a6 100644
--- a/apps/files_sharing/lib/MountProvider.php
+++ b/apps/files_sharing/lib/MountProvider.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -11,6 +12,7 @@ use OCA\Files_Sharing\Event\ShareMountedEvent;
use OCP\Cache\CappedMemoryCache;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Config\IMountProvider;
+use OCP\Files\Mount\IMountManager;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IStorageFactory;
use OCP\ICacheFactory;
@@ -32,6 +34,7 @@ class MountProvider implements IMountProvider {
protected LoggerInterface $logger,
protected IEventDispatcher $eventDispatcher,
protected ICacheFactory $cacheFactory,
+ protected IMountManager $mountManager,
) {
}
@@ -53,26 +56,32 @@ class MountProvider implements IMountProvider {
// filter out excluded shares and group shares that includes self
$shares = array_filter($shares, function (IShare $share) use ($user) {
- return $share->getPermissions() > 0 && $share->getShareOwner() !== $user->getUID();
+ return $share->getPermissions() > 0 && $share->getShareOwner() !== $user->getUID() && $share->getSharedBy() !== $user->getUID();
});
$superShares = $this->buildSuperShares($shares, $user);
+ $otherMounts = $this->mountManager->getAll();
$mounts = [];
$view = new View('/' . $user->getUID() . '/files');
$ownerViews = [];
$sharingDisabledForUser = $this->shareManager->sharingDisabledForUser($user->getUID());
/** @var CappedMemoryCache<bool> $folderExistCache */
$foldersExistCache = new CappedMemoryCache();
+
+ $validShareCache = $this->cacheFactory->createLocal('share-valid-mountpoint-max');
+ $maxValidatedShare = $validShareCache->get($user->getUID()) ?? 0;
+ $newMaxValidatedShare = $maxValidatedShare;
+
foreach ($superShares as $share) {
try {
/** @var IShare $parentShare */
$parentShare = $share[0];
- if ($parentShare->getStatus() !== IShare::STATUS_ACCEPTED &&
- ($parentShare->getShareType() === IShare::TYPE_GROUP ||
- $parentShare->getShareType() === IShare::TYPE_USERGROUP ||
- $parentShare->getShareType() === IShare::TYPE_USER)) {
+ if ($parentShare->getStatus() !== IShare::STATUS_ACCEPTED
+ && ($parentShare->getShareType() === IShare::TYPE_GROUP
+ || $parentShare->getShareType() === IShare::TYPE_USERGROUP
+ || $parentShare->getShareType() === IShare::TYPE_USER)) {
continue;
}
@@ -80,9 +89,10 @@ class MountProvider implements IMountProvider {
if (!isset($ownerViews[$owner])) {
$ownerViews[$owner] = new View('/' . $parentShare->getShareOwner() . '/files');
}
+ $shareId = (int)$parentShare->getId();
$mount = new SharedMount(
'\OCA\Files_Sharing\SharedStorage',
- $mounts,
+ array_merge($mounts, $otherMounts),
[
'user' => $user->getUID(),
// parent share
@@ -97,9 +107,11 @@ class MountProvider implements IMountProvider {
$foldersExistCache,
$this->eventDispatcher,
$user,
- $this->cacheFactory->createLocal('share-valid-mountpoint')
+ ($shareId <= $maxValidatedShare),
);
+ $newMaxValidatedShare = max($shareId, $newMaxValidatedShare);
+
$event = new ShareMountedEvent($mount);
$this->eventDispatcher->dispatchTyped($event);
@@ -118,6 +130,8 @@ class MountProvider implements IMountProvider {
}
}
+ $validShareCache->set($user->getUID(), $newMaxValidatedShare, 24 * 60 * 60);
+
// array_filter removes the null values from the array
return array_values(array_filter($mounts));
}
diff --git a/apps/files_sharing/lib/Notification/Listener.php b/apps/files_sharing/lib/Notification/Listener.php
index c40ad6e8f7b..1cf0f845e7a 100644
--- a/apps/files_sharing/lib/Notification/Listener.php
+++ b/apps/files_sharing/lib/Notification/Listener.php
@@ -40,8 +40,8 @@ class Listener {
$group = $this->groupManager->get($share->getSharedWith());
foreach ($group->getUsers() as $user) {
- if ($user->getUID() === $share->getShareOwner() ||
- $user->getUID() === $share->getSharedBy()) {
+ if ($user->getUID() === $share->getShareOwner()
+ || $user->getUID() === $share->getSharedBy()) {
continue;
}
@@ -72,8 +72,8 @@ class Listener {
continue;
}
- if ($user->getUID() === $share->getShareOwner() ||
- $user->getUID() === $share->getSharedBy()) {
+ if ($user->getUID() === $share->getShareOwner()
+ || $user->getUID() === $share->getSharedBy()) {
continue;
}
diff --git a/apps/files_sharing/lib/Notification/Notifier.php b/apps/files_sharing/lib/Notification/Notifier.php
index 43f61258395..e4434ef0b37 100644
--- a/apps/files_sharing/lib/Notification/Notifier.php
+++ b/apps/files_sharing/lib/Notification/Notifier.php
@@ -67,9 +67,9 @@ class Notifier implements INotifier {
* @since 9.0.0
*/
public function prepare(INotification $notification, string $languageCode): INotification {
- if ($notification->getApp() !== 'files_sharing' ||
- ($notification->getSubject() !== 'expiresTomorrow' &&
- $notification->getObjectType() !== 'share')) {
+ if ($notification->getApp() !== 'files_sharing'
+ || ($notification->getSubject() !== 'expiresTomorrow'
+ && $notification->getObjectType() !== 'share')) {
throw new UnknownNotificationException('Unhandled app or subject');
}
diff --git a/apps/files_sharing/lib/OrphanHelper.php b/apps/files_sharing/lib/OrphanHelper.php
index 4a52af0406c..6e070f1446b 100644
--- a/apps/files_sharing/lib/OrphanHelper.php
+++ b/apps/files_sharing/lib/OrphanHelper.php
@@ -58,8 +58,7 @@ class OrphanHelper {
$query = $this->connection->getQueryBuilder();
$query->select('id', 'file_source', 'uid_owner', 'file_target')
->from('share')
- ->where($query->expr()->eq('item_type', $query->createNamedParameter('file')))
- ->orWhere($query->expr()->eq('item_type', $query->createNamedParameter('folder')));
+ ->where($query->expr()->in('item_type', $query->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)));
$result = $query->executeQuery();
while ($row = $result->fetch()) {
yield [
diff --git a/apps/files_sharing/lib/ResponseDefinitions.php b/apps/files_sharing/lib/ResponseDefinitions.php
index 6b6b0fcc4b6..71a2b25a70c 100644
--- a/apps/files_sharing/lib/ResponseDefinitions.php
+++ b/apps/files_sharing/lib/ResponseDefinitions.php
@@ -22,6 +22,7 @@ namespace OCA\Files_Sharing;
* file_target: string,
* has_preview: bool,
* hide_download: 0|1,
+ * is_trusted_server?: bool,
* is-mount-root: bool,
* id: string,
* item_mtime: int,
diff --git a/apps/files_sharing/lib/Scanner.php b/apps/files_sharing/lib/Scanner.php
index 8a695ce9539..28972c1b462 100644
--- a/apps/files_sharing/lib/Scanner.php
+++ b/apps/files_sharing/lib/Scanner.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/apps/files_sharing/lib/Settings/Personal.php b/apps/files_sharing/lib/Settings/Personal.php
index e2146017dd5..171131b1819 100644
--- a/apps/files_sharing/lib/Settings/Personal.php
+++ b/apps/files_sharing/lib/Settings/Personal.php
@@ -9,6 +9,7 @@ declare(strict_types=1);
namespace OCA\Files_Sharing\Settings;
use OCA\Files_Sharing\AppInfo\Application;
+use OCA\Files_Sharing\Helper;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\IConfig;
@@ -25,16 +26,18 @@ class Personal implements ISettings {
public function getForm(): TemplateResponse {
$defaultAcceptSystemConfig = $this->config->getSystemValueBool('sharing.enable_share_accept', false) ? 'no' : 'yes';
- $shareFolderSystemConfig = $this->config->getSystemValue('share_folder', '/');
+ $defaultShareFolder = $this->config->getSystemValue('share_folder', '/');
+ $userShareFolder = Helper::getShareFolder(userId: $this->userId);
$acceptDefault = $this->config->getUserValue($this->userId, Application::APP_ID, 'default_accept', $defaultAcceptSystemConfig) === 'yes';
$enforceAccept = $this->config->getSystemValueBool('sharing.force_share_accept', false);
$allowCustomDirectory = $this->config->getSystemValueBool('sharing.allow_custom_share_folder', true);
- $shareFolderDefault = $this->config->getUserValue($this->userId, Application::APP_ID, 'share_folder', $shareFolderSystemConfig);
+
$this->initialState->provideInitialState('accept_default', $acceptDefault);
$this->initialState->provideInitialState('enforce_accept', $enforceAccept);
$this->initialState->provideInitialState('allow_custom_share_folder', $allowCustomDirectory);
- $this->initialState->provideInitialState('share_folder', $shareFolderDefault);
- $this->initialState->provideInitialState('default_share_folder', $shareFolderSystemConfig);
+ $this->initialState->provideInitialState('default_share_folder', $defaultShareFolder);
+ $this->initialState->provideInitialState('share_folder', $userShareFolder);
+
return new TemplateResponse('files_sharing', 'Settings/personal');
}
diff --git a/apps/files_sharing/lib/ShareBackend/File.php b/apps/files_sharing/lib/ShareBackend/File.php
index 56a4aa47e60..2aa52ef1b7f 100644
--- a/apps/files_sharing/lib/ShareBackend/File.php
+++ b/apps/files_sharing/lib/ShareBackend/File.php
@@ -12,6 +12,7 @@ use OC\Files\View;
use OCA\FederatedFileSharing\FederatedShareProvider;
use OCA\Files_Sharing\Helper;
use OCP\Files\NotFoundException;
+use OCP\IDBConnection;
use OCP\Server;
use OCP\Share\IShare;
use OCP\Share_Backend_File_Dependent;
@@ -34,7 +35,7 @@ class File implements Share_Backend_File_Dependent {
if ($federatedShareProvider) {
$this->federatedShareProvider = $federatedShareProvider;
} else {
- $this->federatedShareProvider = \OC::$server->query(FederatedShareProvider::class);
+ $this->federatedShareProvider = Server::get(FederatedShareProvider::class);
}
}
@@ -183,7 +184,7 @@ class File implements Share_Backend_File_Dependent {
if (isset($source['parent'])) {
$parent = $source['parent'];
while (isset($parent)) {
- $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
+ $qb = Server::get(IDBConnection::class)->getQueryBuilder();
$qb->select('parent', 'uid_owner')
->from('share')
->where(
diff --git a/apps/files_sharing/lib/ShareBackend/Folder.php b/apps/files_sharing/lib/ShareBackend/Folder.php
index fefdd7f667c..df5529c3c4a 100644
--- a/apps/files_sharing/lib/ShareBackend/Folder.php
+++ b/apps/files_sharing/lib/ShareBackend/Folder.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -6,6 +7,8 @@
*/
namespace OCA\Files_Sharing\ShareBackend;
+use OCP\IDBConnection;
+use OCP\Server;
use OCP\Share_Backend_Collection;
class Folder extends File implements Share_Backend_Collection {
@@ -13,7 +16,7 @@ class Folder extends File implements Share_Backend_Collection {
$children = [];
$parents = [$itemSource];
- $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
+ $qb = Server::get(IDBConnection::class)->getQueryBuilder();
$qb->select('id')
->from('mimetypes')
->where(
@@ -29,7 +32,7 @@ class Folder extends File implements Share_Backend_Collection {
$mimetype = -1;
}
while (!empty($parents)) {
- $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
+ $qb = Server::get(IDBConnection::class)->getQueryBuilder();
$parents = array_map(function ($parent) use ($qb) {
return $qb->createNamedParameter($parent);
diff --git a/apps/files_sharing/lib/SharedMount.php b/apps/files_sharing/lib/SharedMount.php
index 5f7e8f376f1..692a6c8979b 100644
--- a/apps/files_sharing/lib/SharedMount.php
+++ b/apps/files_sharing/lib/SharedMount.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -16,7 +17,7 @@ use OCP\Cache\CappedMemoryCache;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Events\InvalidateMountCacheEvent;
use OCP\Files\Storage\IStorageFactory;
-use OCP\ICache;
+use OCP\IDBConnection;
use OCP\IUser;
use OCP\Server;
use OCP\Share\Events\VerifyMountPointEvent;
@@ -47,13 +48,19 @@ class SharedMount extends MountPoint implements MoveableMount, ISharedMountPoint
CappedMemoryCache $folderExistCache,
private IEventDispatcher $eventDispatcher,
private IUser $user,
- private ICache $cache,
+ bool $alreadyVerified,
) {
$this->superShare = $arguments['superShare'];
$this->groupedShares = $arguments['groupedShares'];
- $newMountPoint = $this->verifyMountPoint($this->superShare, $mountpoints, $folderExistCache);
- $absMountPoint = '/' . $user->getUID() . '/files' . $newMountPoint;
+ $absMountPoint = '/' . $user->getUID() . '/files/' . trim($this->superShare->getTarget(), '/') . '/';
+
+ // after the mountpoint is verified for the first time, only new mountpoints (e.g. groupfolders can overwrite the target)
+ if (!$alreadyVerified || isset($mountpoints[$absMountPoint])) {
+ $newMountPoint = $this->verifyMountPoint($this->superShare, $mountpoints, $folderExistCache);
+ $absMountPoint = '/' . $user->getUID() . '/files/' . trim($newMountPoint, '/') . '/';
+ }
+
parent::__construct($storage, $absMountPoint, $arguments, $loader, null, null, MountProvider::class);
}
@@ -70,12 +77,6 @@ class SharedMount extends MountPoint implements MoveableMount, ISharedMountPoint
array $mountpoints,
CappedMemoryCache $folderExistCache,
) {
- $cacheKey = $this->user->getUID() . '/' . $share->getId() . '/' . $share->getTarget();
- $cached = $this->cache->get($cacheKey);
- if ($cached !== null) {
- return $cached;
- }
-
$mountPoint = basename($share->getTarget());
$parent = dirname($share->getTarget());
@@ -104,8 +105,6 @@ class SharedMount extends MountPoint implements MoveableMount, ISharedMountPoint
$this->updateFileTarget($newMountPoint, $share);
}
- $this->cache->set($cacheKey, $newMountPoint, 60 * 60);
-
return $newMountPoint;
}
@@ -121,7 +120,7 @@ class SharedMount extends MountPoint implements MoveableMount, ISharedMountPoint
foreach ($this->groupedShares as $tmpShare) {
$tmpShare->setTarget($newPath);
- \OC::$server->getShareManager()->moveShare($tmpShare, $this->user->getUID());
+ Server::get(\OCP\Share\IManager::class)->moveShare($tmpShare, $this->user->getUID());
}
$this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent($this->user));
@@ -249,7 +248,7 @@ class SharedMount extends MountPoint implements MoveableMount, ISharedMountPoint
if (!is_null($this->getShare()->getNodeCacheEntry())) {
return $this->getShare()->getNodeCacheEntry()->getStorageId();
} else {
- $builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
+ $builder = Server::get(IDBConnection::class)->getQueryBuilder();
$query = $builder->select('storage')
->from('filecache')
diff --git a/apps/files_sharing/lib/SharedStorage.php b/apps/files_sharing/lib/SharedStorage.php
index f54252d0458..e310c5f3138 100644
--- a/apps/files_sharing/lib/SharedStorage.php
+++ b/apps/files_sharing/lib/SharedStorage.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -36,6 +37,7 @@ use OCP\Files\Storage\ILockingStorage;
use OCP\Files\Storage\ISharedStorage;
use OCP\Files\Storage\IStorage;
use OCP\Lock\ILockingProvider;
+use OCP\Server;
use OCP\Share\IShare;
use OCP\Util;
use Psr\Log\LoggerInterface;
@@ -90,7 +92,7 @@ class SharedStorage extends Jail implements LegacyISharedStorage, ISharedStorage
public function __construct(array $parameters) {
$this->ownerView = $parameters['ownerView'];
- $this->logger = \OC::$server->get(LoggerInterface::class);
+ $this->logger = Server::get(LoggerInterface::class);
$this->superShare = $parameters['superShare'];
$this->groupedShares = $parameters['groupedShares'];
@@ -150,7 +152,7 @@ class SharedStorage extends Jail implements LegacyISharedStorage, ISharedStorage
}
/** @var IRootFolder $rootFolder */
- $rootFolder = \OC::$server->get(IRootFolder::class);
+ $rootFolder = Server::get(IRootFolder::class);
$this->ownerUserFolder = $rootFolder->getUserFolder($this->superShare->getShareOwner());
$sourceId = $this->superShare->getNodeId();
$ownerNodes = $this->ownerUserFolder->getById($sourceId);
@@ -412,7 +414,7 @@ class SharedStorage extends Jail implements LegacyISharedStorage, ISharedStorage
$this->cache = new Cache(
$storage,
$sourceRoot,
- \OC::$server->get(CacheDependencies::class),
+ Server::get(CacheDependencies::class),
$this->getShare()
);
return $this->cache;
@@ -437,11 +439,15 @@ class SharedStorage extends Jail implements LegacyISharedStorage, ISharedStorage
// Get node information
$node = $this->getShare()->getNodeCacheEntry();
if ($node instanceof CacheEntry) {
- $storageId = $node->getData()['storage_string_id'];
+ $storageId = $node->getData()['storage_string_id'] ?? null;
// for shares from the home storage we can rely on the home storage to keep itself up to date
// for other storages we need use the proper watcher
- if (!(str_starts_with($storageId, 'home::') || str_starts_with($storageId, 'object::user'))) {
+ if ($storageId !== null && !(str_starts_with($storageId, 'home::') || str_starts_with($storageId, 'object::user'))) {
+ $cache = $this->getCache();
$this->watcher = parent::getWatcher($path, $storage);
+ if ($cache instanceof Cache) {
+ $this->watcher->onUpdate($cache->markRootChanged(...));
+ }
return $this->watcher;
}
}
@@ -458,7 +464,7 @@ class SharedStorage extends Jail implements LegacyISharedStorage, ISharedStorage
*/
public function unshareStorage(): bool {
foreach ($this->groupedShares as $share) {
- \OC::$server->getShareManager()->deleteFromSelf($share, $this->user);
+ Server::get(\OCP\Share\IManager::class)->deleteFromSelf($share, $this->user);
}
return true;
}
@@ -550,4 +556,9 @@ class SharedStorage extends Jail implements LegacyISharedStorage, ISharedStorage
$this->init();
return parent::getUnjailedPath($path);
}
+
+ public function getDirectDownload(string $path): array|false {
+ // disable direct download for shares
+ return [];
+ }
}
diff --git a/apps/files_sharing/lib/Updater.php b/apps/files_sharing/lib/Updater.php
index 226cf3e7fd4..24e82330d43 100644
--- a/apps/files_sharing/lib/Updater.php
+++ b/apps/files_sharing/lib/Updater.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -11,6 +12,7 @@ use OC\Files\Filesystem;
use OC\Files\Mount\MountPoint;
use OCP\Constants;
use OCP\Files\Folder;
+use OCP\Files\Mount\IMountManager;
use OCP\Server;
use OCP\Share\IShare;
@@ -48,7 +50,7 @@ class Updater {
$src = $userFolder->get($path);
- $shareManager = \OC::$server->getShareManager();
+ $shareManager = Server::get(\OCP\Share\IManager::class);
// We intentionally include invalid shares, as they have been automatically invalidated due to the node no longer
// being accessible for the user. Only in this case where we adjust the share after it was moved we want to ignore
@@ -74,8 +76,8 @@ class Updater {
foreach ($subShares as $subShare) {
$shareCacheEntry = $shareSources[$subShare->getNodeId()] ?? null;
if (
- $shareCacheEntry &&
- str_starts_with($shareCacheEntry->getPath(), $sourceInternalPath . '/')
+ $shareCacheEntry
+ && str_starts_with($shareCacheEntry->getPath(), $sourceInternalPath . '/')
) {
$shares[] = $subShare;
}
@@ -88,15 +90,15 @@ class Updater {
}
// Check if the destination is inside a share
- $mountManager = \OC::$server->getMountManager();
+ $mountManager = Server::get(IMountManager::class);
$dstMount = $mountManager->find($src->getPath());
//Ownership is moved over
foreach ($shares as $share) {
if (
- $share->getShareType() !== IShare::TYPE_USER &&
- $share->getShareType() !== IShare::TYPE_GROUP &&
- $share->getShareType() !== IShare::TYPE_ROOM
+ $share->getShareType() !== IShare::TYPE_USER
+ && $share->getShareType() !== IShare::TYPE_GROUP
+ && $share->getShareType() !== IShare::TYPE_ROOM
) {
continue;
}
diff --git a/apps/files_sharing/lib/ViewOnly.php b/apps/files_sharing/lib/ViewOnly.php
index 2204d26388b..e075677248a 100644
--- a/apps/files_sharing/lib/ViewOnly.php
+++ b/apps/files_sharing/lib/ViewOnly.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2019 ownCloud GmbH