diff options
Diffstat (limited to 'apps/files_sharing/lib')
12 files changed, 130 insertions, 23 deletions
diff --git a/apps/files_sharing/lib/Activity/Settings/PublicLinksUpload.php b/apps/files_sharing/lib/Activity/Settings/PublicLinksUpload.php index 00bf7d0dde9..bcdc4a4ff97 100644 --- a/apps/files_sharing/lib/Activity/Settings/PublicLinksUpload.php +++ b/apps/files_sharing/lib/Activity/Settings/PublicLinksUpload.php @@ -61,6 +61,6 @@ class PublicLinksUpload extends ShareActivitySettings { * @since 11.0.0 */ public function isDefaultEnabledMail() { - return true; + return false; } } diff --git a/apps/files_sharing/lib/AppInfo/Application.php b/apps/files_sharing/lib/AppInfo/Application.php index 4c47d3fc2c0..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,6 +13,7 @@ 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; @@ -106,6 +108,8 @@ class Application extends App implements IBootstrap { // 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 ccc93eb0952..024df5d3f43 100644 --- a/apps/files_sharing/lib/Cache.php +++ b/apps/files_sharing/lib/Cache.php @@ -68,7 +68,7 @@ class Cache extends CacheJail { return $this->root; } - protected function getGetUnjailedRoot(): string { + public function getGetUnjailedRoot(): string { return $this->sourceRootInfo->getPath(); } diff --git a/apps/files_sharing/lib/Config/ConfigLexicon.php b/apps/files_sharing/lib/Config/ConfigLexicon.php new file mode 100644 index 00000000000..a463b4e7ef2 --- /dev/null +++ b/apps/files_sharing/lib/Config/ConfigLexicon.php @@ -0,0 +1,39 @@ +<?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 NCU\Config\Lexicon\ConfigLexiconEntry; +use NCU\Config\Lexicon\ConfigLexiconStrictness; +use NCU\Config\Lexicon\IConfigLexicon; +use NCU\Config\ValueType; + +/** + * Config Lexicon for files_sharing. + * + * Please Add & Manage your Config Keys in that file and keep the Lexicon up to date! + * + * {@see IConfigLexicon} + */ +class ConfigLexicon implements IConfigLexicon { + public const SHOW_FEDERATED_AS_INTERNAL = 'show_federated_shares_as_internal'; + + public function getStrictness(): ConfigLexiconStrictness { + return ConfigLexiconStrictness::IGNORE; + } + + public function getAppConfigs(): array { + return [ + new ConfigLexiconEntry(self::SHOW_FEDERATED_AS_INTERNAL, ValueType::BOOL, false, 'shows federated shares as internal shares', true), + ]; + } + + public function getUserConfigs(): array { + return []; + } +} diff --git a/apps/files_sharing/lib/Controller/PublicPreviewController.php b/apps/files_sharing/lib/Controller/PublicPreviewController.php index 91dead57e80..f275001f162 100644 --- a/apps/files_sharing/lib/Controller/PublicPreviewController.php +++ b/apps/files_sharing/lib/Controller/PublicPreviewController.php @@ -11,6 +11,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 +19,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 +35,7 @@ class PublicPreviewController extends PublicShareController { private ShareManager $shareManager, ISession $session, private IPreview $previewManager, + private IMimeIconProvider $mimeIconProvider, ) { parent::__construct($appName, $request, $session); } @@ -63,9 +66,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 +84,7 @@ class PublicPreviewController extends PublicShareController { int $x = 32, int $y = 32, $a = false, + bool $mimeFallback = false, ) { $cacheForSeconds = 60 * 60 * 24; // 1 day @@ -101,7 +107,7 @@ class PublicPreviewController extends PublicShareController { $downloadForbidden = $attributes?->getAttribute('permissions', 'download') === false; // 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 +130,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); diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index 81ead3c4978..23ba9da1568 100644 --- a/apps/files_sharing/lib/Controller/ShareAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareAPIController.php @@ -94,6 +94,7 @@ class ShareAPIController extends OCSController { private LoggerInterface $logger, private IProviderFactory $factory, private IMailer $mailer, + private ITagManager $tagManager, private ?string $userId = null, ) { parent::__construct($appName, $request); @@ -472,7 +473,7 @@ class ShareAPIController extends OCSController { $share = $this->formatShare($share); if ($include_tags) { - $share = Helper::populateTags([$share], Server::get(ITagManager::class)); + $share = $this->populateTags([$share]); } else { $share = [$share]; } @@ -847,7 +848,7 @@ class ShareAPIController extends OCSController { } if ($includeTags) { - $formatted = Helper::populateTags($formatted, Server::get(ITagManager::class)); + $formatted = $this->populateTags($formatted); } return $formatted; @@ -1100,8 +1101,7 @@ class ShareAPIController extends OCSController { $formatted = $this->fixMissingDisplayName($formatted); if ($includeTags) { - $formatted = - Helper::populateTags($formatted, Server::get(ITagManager::class)); + $formatted = $this->populateTags($formatted); } return $formatted; @@ -2118,6 +2118,8 @@ class ShareAPIController extends OCSController { $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 } } @@ -2219,4 +2221,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/DefaultPublicShareTemplateProvider.php b/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php index 686ba32fd49..afba45cac4a 100644 --- a/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php +++ b/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php @@ -91,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); @@ -104,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)) { @@ -149,10 +151,7 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider $headerActions = []; 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()); diff --git a/apps/files_sharing/lib/External/Scanner.php b/apps/files_sharing/lib/External/Scanner.php index e45d3723923..2b0741b4b96 100644 --- a/apps/files_sharing/lib/External/Scanner.php +++ b/apps/files_sharing/lib/External/Scanner.php @@ -6,6 +6,7 @@ */ namespace OCA\Files_Sharing\External; +use OC\Files\Cache\CacheEntry; use OC\ForbiddenException; use OCP\Files\NotFoundException; use OCP\Files\StorageInvalidException; @@ -29,7 +30,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/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..9f0eee9159a 100644 --- a/apps/files_sharing/lib/Listener/LoadSidebarListener.php +++ b/apps/files_sharing/lib/Listener/LoadSidebarListener.php @@ -11,9 +11,12 @@ 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\IAppConfig; +use OCP\Server; use OCP\Share\IManager; use OCP\Util; @@ -33,6 +36,8 @@ class LoadSidebarListener implements IEventListener { return; } + $appConfig = Server::get(IAppConfig::class); + $this->initialState->provideInitialState('showFederatedSharesAsInternal', $appConfig->getValueBool('files_sharing', ConfigLexicon::SHOW_FEDERATED_AS_INTERNAL)); Util::addScript(Application::APP_ID, 'files_sharing_tab', 'files'); } } diff --git a/apps/files_sharing/lib/MountProvider.php b/apps/files_sharing/lib/MountProvider.php index ea99023676b..91c392de6eb 100644 --- a/apps/files_sharing/lib/MountProvider.php +++ b/apps/files_sharing/lib/MountProvider.php @@ -60,7 +60,8 @@ class MountProvider implements IMountProvider { $superShares = $this->buildSuperShares($shares, $user); - $mounts = $this->mountManager->getAll(); + $otherMounts = $this->mountManager->getAll(); + $mounts = []; $view = new View('/' . $user->getUID() . '/files'); $ownerViews = []; $sharingDisabledForUser = $this->shareManager->sharingDisabledForUser($user->getUID()); @@ -90,7 +91,7 @@ class MountProvider implements IMountProvider { $shareId = (int)$parentShare->getId(); $mount = new SharedMount( '\OCA\Files_Sharing\SharedStorage', - $mounts, + array_merge($mounts, $otherMounts), [ 'user' => $user->getUID(), // parent share @@ -105,7 +106,7 @@ class MountProvider implements IMountProvider { $foldersExistCache, $this->eventDispatcher, $user, - ($shareId <= $maxValidatedShare) + ($shareId <= $maxValidatedShare), ); $newMaxValidatedShare = max($shareId, $newMaxValidatedShare); diff --git a/apps/files_sharing/lib/SharedStorage.php b/apps/files_sharing/lib/SharedStorage.php index dfd4854de1f..1014b0d37d9 100644 --- a/apps/files_sharing/lib/SharedStorage.php +++ b/apps/files_sharing/lib/SharedStorage.php @@ -555,4 +555,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 []; + } } |