diff --git a/apps/dav/appinfo/v1/publicwebdav.php b/apps/dav/appinfo/v1/publicwebdav.php
index 5ef383e1dd5..38753374150 100644
--- a/apps/dav/appinfo/v1/publicwebdav.php
+++ b/apps/dav/appinfo/v1/publicwebdav.php
@@ -87,6 +87,7 @@ $server = $serverFactory->createServer($baseuri, $requestUri, $authPlugin, funct
$view = new \OC\Files\View($node->getPath());
$filesDropPlugin->setView($view);
+ $filesDropPlugin->setShare($share);
return $view;
});
diff --git a/apps/dav/appinfo/v2/publicremote.php b/apps/dav/appinfo/v2/publicremote.php
index bdc4169dd4e..44cf4214505 100644
--- a/apps/dav/appinfo/v2/publicremote.php
+++ b/apps/dav/appinfo/v2/publicremote.php
@@ -116,6 +116,7 @@ $server = $serverFactory->createServer($baseuri, $requestUri, $authPlugin, funct
$view = new View($node->getPath());
$filesDropPlugin->setView($view);
+ $filesDropPlugin->setShare($share);
return $view;
});
diff --git a/apps/dav/lib/Files/Sharing/FilesDropPlugin.php b/apps/dav/lib/Files/Sharing/FilesDropPlugin.php
index c4d1957c67e..b364c4ebbfc 100644
--- a/apps/dav/lib/Files/Sharing/FilesDropPlugin.php
+++ b/apps/dav/lib/Files/Sharing/FilesDropPlugin.php
@@ -6,6 +6,7 @@
namespace OCA\DAV\Files\Sharing;
use OC\Files\View;
+use OCP\Share\IShare;
use Sabre\DAV\Exception\MethodNotAllowed;
use Sabre\DAV\ServerPlugin;
use Sabre\HTTP\RequestInterface;
@@ -16,17 +17,19 @@ use Sabre\HTTP\ResponseInterface;
*/
class FilesDropPlugin extends ServerPlugin {
- /** @var View */
- private $view;
+ private ?View $view = null;
+ private ?IShare $share = null;
+ private bool $enabled = false;
- /** @var bool */
- private $enabled = false;
-
- public function setView(View $view) {
+ public function setView(View $view): void {
$this->view = $view;
}
- public function enable() {
+ public function setShare(IShare $share): void {
+ $this->share = $share;
+ }
+
+ public function enable(): void {
$this->enabled = true;
}
@@ -39,25 +42,53 @@ class FilesDropPlugin extends ServerPlugin {
* @return void
* @throws MethodNotAllowed
*/
- public function initialize(\Sabre\DAV\Server $server) {
+ public function initialize(\Sabre\DAV\Server $server): void {
$server->on('beforeMethod:*', [$this, 'beforeMethod'], 999);
$this->enabled = false;
}
- public function beforeMethod(RequestInterface $request, ResponseInterface $response) {
- if (!$this->enabled) {
+ public function beforeMethod(RequestInterface $request, ResponseInterface $response): void {
+ if (!$this->enabled || $this->share === null || $this->view === null) {
return;
}
- if ($request->getMethod() !== 'PUT' && $request->getMethod() !== 'MKCOL') {
+ // Only allow file drop
+ if ($request->getMethod() !== 'PUT') {
throw new MethodNotAllowed('Only PUT is allowed on files drop');
}
+ // Always upload at the root level
$path = explode('/', $request->getPath());
$path = array_pop($path);
+ // Extract the attributes for the file request
+ $attributes = $this->share->getAttributes();
+ if ($attributes === null) {
+ return;
+ }
+
+ // Prepare file request
+ $nickName = $request->getHeader('X-NC-Nickname');
+ $isFileRequest = $attributes->getAttribute('fileRequest', 'enabled') === true;
+
+ // We need a valid nickname for file requests
+ if ($isFileRequest && ($nickName == null || trim($nickName) === '')) {
+ throw new MethodNotAllowed('Nickname is required for file requests');
+ }
+
+ // If this is a file request we need to create a folder for the user
+ if ($isFileRequest) {
+ // Check if the folder already exists
+ if (!($this->view->file_exists($nickName) === true)) {
+ $this->view->mkdir($nickName);
+ }
+ // Put all files in the subfolder
+ $path = $nickName . '/' . $path;
+ }
+
$newName = \OC_Helper::buildNotExistingFileNameForView('/', $path, $this->view);
$url = $request->getBaseUrl() . $newName;
$request->setUrl($url);
}
+
}
diff --git a/apps/dav/tests/unit/Files/Sharing/FilesDropPluginTest.php b/apps/dav/tests/unit/Files/Sharing/FilesDropPluginTest.php
index 9a077e35076..7264119f8c6 100644
--- a/apps/dav/tests/unit/Files/Sharing/FilesDropPluginTest.php
+++ b/apps/dav/tests/unit/Files/Sharing/FilesDropPluginTest.php
@@ -7,6 +7,8 @@ namespace OCA\DAV\Tests\Files\Sharing;
use OC\Files\View;
use OCA\DAV\Files\Sharing\FilesDropPlugin;
+use OCP\Share\IAttributes;
+use OCP\Share\IShare;
use Sabre\DAV\Exception\MethodNotAllowed;
use Sabre\DAV\Server;
use Sabre\HTTP\RequestInterface;
@@ -18,6 +20,9 @@ class FilesDropPluginTest extends TestCase {
/** @var View|\PHPUnit\Framework\MockObject\MockObject */
private $view;
+ /** @var IShare|\PHPUnit\Framework\MockObject\MockObject */
+ private $share;
+
/** @var Server|\PHPUnit\Framework\MockObject\MockObject */
private $server;
@@ -34,6 +39,7 @@ class FilesDropPluginTest extends TestCase {
parent::setUp();
$this->view = $this->createMock(View::class);
+ $this->share = $this->createMock(IShare::class);
$this->server = $this->createMock(Server::class);
$this->plugin = new FilesDropPlugin();
@@ -42,6 +48,11 @@ class FilesDropPluginTest extends TestCase {
$this->response->expects($this->never())
->method($this->anything());
+
+ $attributes = $this->createMock(IAttributes::class);
+ $this->share->expects($this->any())
+ ->method('getAttributes')
+ ->willReturn($attributes);
}
public function testInitialize(): void {
@@ -69,6 +80,7 @@ class FilesDropPluginTest extends TestCase {
public function testValid(): void {
$this->plugin->enable();
$this->plugin->setView($this->view);
+ $this->plugin->setShare($this->share);
$this->request->method('getMethod')
->willReturn('PUT');
@@ -93,6 +105,7 @@ class FilesDropPluginTest extends TestCase {
public function testFileAlreadyExistsValid(): void {
$this->plugin->enable();
$this->plugin->setView($this->view);
+ $this->plugin->setShare($this->share);
$this->request->method('getMethod')
->willReturn('PUT');
@@ -122,6 +135,7 @@ class FilesDropPluginTest extends TestCase {
public function testNoMKCOL(): void {
$this->plugin->enable();
$this->plugin->setView($this->view);
+ $this->plugin->setShare($this->share);
$this->request->method('getMethod')
->willReturn('MKCOL');
@@ -134,6 +148,7 @@ class FilesDropPluginTest extends TestCase {
public function testNoSubdirPut(): void {
$this->plugin->enable();
$this->plugin->setView($this->view);
+ $this->plugin->setShare($this->share);
$this->request->method('getMethod')
->willReturn('PUT');
diff --git a/apps/files_sharing/composer/composer/autoload_classmap.php b/apps/files_sharing/composer/composer/autoload_classmap.php
index e1abddb3a64..b7a931a6228 100644
--- a/apps/files_sharing/composer/composer/autoload_classmap.php
+++ b/apps/files_sharing/composer/composer/autoload_classmap.php
@@ -59,6 +59,7 @@ return array(
'OCA\\Files_Sharing\\Listener\\BeforeDirectFileDownloadListener' => $baseDir . '/../lib/Listener/BeforeDirectFileDownloadListener.php',
'OCA\\Files_Sharing\\Listener\\BeforeZipCreatedListener' => $baseDir . '/../lib/Listener/BeforeZipCreatedListener.php',
'OCA\\Files_Sharing\\Listener\\LoadAdditionalListener' => $baseDir . '/../lib/Listener/LoadAdditionalListener.php',
+ 'OCA\\Files_Sharing\\Listener\\LoadPublicFileRequestAuthListener' => $baseDir . '/../lib/Listener/LoadPublicFileRequestAuthListener.php',
'OCA\\Files_Sharing\\Listener\\LoadSidebarListener' => $baseDir . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files_Sharing\\Listener\\ShareInteractionListener' => $baseDir . '/../lib/Listener/ShareInteractionListener.php',
'OCA\\Files_Sharing\\Listener\\UserAddedToGroupListener' => $baseDir . '/../lib/Listener/UserAddedToGroupListener.php',
diff --git a/apps/files_sharing/composer/composer/autoload_static.php b/apps/files_sharing/composer/composer/autoload_static.php
index 5d2fb3bac2a..70dc7be7cdf 100644
--- a/apps/files_sharing/composer/composer/autoload_static.php
+++ b/apps/files_sharing/composer/composer/autoload_static.php
@@ -74,6 +74,7 @@ class ComposerStaticInitFiles_Sharing
'OCA\\Files_Sharing\\Listener\\BeforeDirectFileDownloadListener' => __DIR__ . '/..' . '/../lib/Listener/BeforeDirectFileDownloadListener.php',
'OCA\\Files_Sharing\\Listener\\BeforeZipCreatedListener' => __DIR__ . '/..' . '/../lib/Listener/BeforeZipCreatedListener.php',
'OCA\\Files_Sharing\\Listener\\LoadAdditionalListener' => __DIR__ . '/..' . '/../lib/Listener/LoadAdditionalListener.php',
+ 'OCA\\Files_Sharing\\Listener\\LoadPublicFileRequestAuthListener' => __DIR__ . '/..' . '/../lib/Listener/LoadPublicFileRequestAuthListener.php',
'OCA\\Files_Sharing\\Listener\\LoadSidebarListener' => __DIR__ . '/..' . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files_Sharing\\Listener\\ShareInteractionListener' => __DIR__ . '/..' . '/../lib/Listener/ShareInteractionListener.php',
'OCA\\Files_Sharing\\Listener\\UserAddedToGroupListener' => __DIR__ . '/..' . '/../lib/Listener/UserAddedToGroupListener.php',
diff --git a/apps/files_sharing/js/files_drop.js b/apps/files_sharing/js/files_drop.js
index fd9b796ee2c..450af078af2 100644
--- a/apps/files_sharing/js/files_drop.js
+++ b/apps/files_sharing/js/files_drop.js
@@ -22,7 +22,7 @@
// note: password not be required, the endpoint
// will recognize previous validation from the session
root: OC.getRootPath() + '/public.php/dav/files/' + $('#sharingToken').val() + '/',
- useHTTPS: OC.getProtocol() === 'https'
+ useHTTPS: OC.getProtocol() === 'https',
});
// We only process one file at a time 🤷♀️
@@ -47,6 +47,10 @@
data.headers = {};
}
+ if (localStorage.getItem('nick') !== null) {
+ data.headers['X-NC-Nickname'] = localStorage.getItem('nick')
+ }
+
$('#drop-upload-done-indicator').addClass('hidden');
$('#drop-upload-progress-indicator').removeClass('hidden');
diff --git a/apps/files_sharing/lib/AppInfo/Application.php b/apps/files_sharing/lib/AppInfo/Application.php
index 82a5981febf..98c2d280856 100644
--- a/apps/files_sharing/lib/AppInfo/Application.php
+++ b/apps/files_sharing/lib/AppInfo/Application.php
@@ -18,6 +18,7 @@ use OCA\Files_Sharing\Helper;
use OCA\Files_Sharing\Listener\BeforeDirectFileDownloadListener;
use OCA\Files_Sharing\Listener\BeforeZipCreatedListener;
use OCA\Files_Sharing\Listener\LoadAdditionalListener;
+use OCA\Files_Sharing\Listener\LoadPublicFileRequestAuthListener;
use OCA\Files_Sharing\Listener\LoadSidebarListener;
use OCA\Files_Sharing\Listener\ShareInteractionListener;
use OCA\Files_Sharing\Listener\UserAddedToGroupListener;
@@ -34,6 +35,7 @@ use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
+use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
use OCP\Collaboration\Resources\LoadAdditionalScriptsEvent as ResourcesLoadAdditionalScriptsEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Federation\ICloudIdManager;
@@ -85,7 +87,7 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(GroupChangedEvent::class, GroupDisplayNameCache::class);
$context->registerEventListener(GroupDeletedEvent::class, GroupDisplayNameCache::class);
- // sidebar and files scripts
+ // Sidebar and files scripts
$context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadAdditionalListener::class);
$context->registerEventListener(LoadSidebar::class, LoadSidebarListener::class);
$context->registerEventListener(ShareCreatedEvent::class, ShareInteractionListener::class);
@@ -95,6 +97,9 @@ class Application extends App implements IBootstrap {
// Handle download events for view only checks
$context->registerEventListener(BeforeZipCreatedEvent::class, BeforeZipCreatedListener::class);
$context->registerEventListener(BeforeDirectFileDownloadEvent::class, BeforeDirectFileDownloadListener::class);
+
+ // File request auth
+ $context->registerEventListener(BeforeTemplateRenderedEvent::class, LoadPublicFileRequestAuthListener::class);
}
public function boot(IBootContext $context): void {
diff --git a/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php b/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php
index e125100eb68..477bc9f82ce 100644
--- a/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php
+++ b/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php
@@ -19,6 +19,7 @@ use OCP\AppFramework\Http\Template\LinkMenuAction;
use OCP\AppFramework\Http\Template\PublicTemplateResponse;
use OCP\AppFramework\Http\Template\SimpleMenuAction;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
use OCP\Constants;
use OCP\Defaults;
use OCP\EventDispatcher\IEventDispatcher;
@@ -37,39 +38,20 @@ use OCP\Template;
use OCP\Util;
class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider {
- private IUserManager $userManager;
- private IAccountManager $accountManager;
- private IPreview $previewManager;
- protected FederatedShareProvider $federatedShareProvider;
- private IURLGenerator $urlGenerator;
- private IEventDispatcher $eventDispatcher;
- private IL10N $l10n;
- private Defaults $defaults;
- private IConfig $config;
- private IRequest $request;
public function __construct(
- IUserManager $userManager,
- IAccountManager $accountManager,
- IPreview $previewManager,
- FederatedShareProvider $federatedShareProvider,
- IUrlGenerator $urlGenerator,
- IEventDispatcher $eventDispatcher,
- IL10N $l10n,
- Defaults $defaults,
- IConfig $config,
- IRequest $request
+ private IUserManager $userManager,
+ private IAccountManager $accountManager,
+ private IPreview $previewManager,
+ protected FederatedShareProvider $federatedShareProvider,
+ private IUrlGenerator $urlGenerator,
+ private IEventDispatcher $eventDispatcher,
+ private IL10N $l10n,
+ private Defaults $defaults,
+ private IConfig $config,
+ private IRequest $request,
+ private IInitialState $initialState,
) {
- $this->userManager = $userManager;
- $this->accountManager = $accountManager;
- $this->previewManager = $previewManager;
- $this->federatedShareProvider = $federatedShareProvider;
- $this->urlGenerator = $urlGenerator;
- $this->eventDispatcher = $eventDispatcher;
- $this->l10n = $l10n;
- $this->defaults = $defaults;
- $this->config = $config;
- $this->request = $request;
}
public function shouldRespond(IShare $share): bool {
@@ -91,9 +73,16 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider
if ($ownerName->getScope() === IAccountManager::SCOPE_PUBLISHED) {
$shareTmpl['owner'] = $owner->getUID();
$shareTmpl['shareOwner'] = $owner->getDisplayName();
+ $this->initialState->provideInitialState('owner', $shareTmpl['owner']);
+ $this->initialState->provideInitialState('ownerDisplayName', $shareTmpl['shareOwner']);
}
}
+ // Provide initial state
+ $this->initialState->provideInitialState('label', $share->getLabel());
+ $this->initialState->provideInitialState('note', $share->getNote());
+ $this->initialState->provideInitialState('filename', $shareNode->getName());
+
$shareTmpl['filename'] = $shareNode->getName();
$shareTmpl['directory_path'] = $share->getTarget();
$shareTmpl['label'] = $share->getLabel();
diff --git a/apps/files_sharing/lib/Listener/LoadPublicFileRequestAuthListener.php b/apps/files_sharing/lib/Listener/LoadPublicFileRequestAuthListener.php
new file mode 100644
index 00000000000..f1e054c7ee5
--- /dev/null
+++ b/apps/files_sharing/lib/Listener/LoadPublicFileRequestAuthListener.php
@@ -0,0 +1,59 @@
+ */
+class LoadPublicFileRequestAuthListener implements IEventListener {
+ public function __construct(
+ private IManager $shareManager,
+ ) {
+ }
+
+ public function handle(Event $event): void {
+ if (!$event instanceof BeforeTemplateRenderedEvent) {
+ return;
+ }
+
+ // Make sure we are on a public page rendering
+ if ($event->getResponse()->getRenderAs() !== TemplateResponse::RENDER_AS_PUBLIC) {
+ return;
+ }
+
+ $token = $event->getResponse()->getParams()['sharingToken'] ?? null;
+ if ($token === null || $token === '') {
+ return;
+ }
+
+ // Check if the share is a file request
+ $isFileRequest = false;
+ try {
+ $share = $this->shareManager->getShareByToken($token);
+ $attributes = $share->getAttributes();
+ if ($attributes === null) {
+ return;
+ }
+
+ $isFileRequest = $attributes->getAttribute('fileRequest', 'enabled') === true;
+ } catch (\Exception $e) {
+ // 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');
+ }
+ }
+}
diff --git a/apps/files_sharing/src/components/NewFileRequestDialog.vue b/apps/files_sharing/src/components/NewFileRequestDialog.vue
index 398fd976f02..4c476af9bc8 100644
--- a/apps/files_sharing/src/components/NewFileRequestDialog.vue
+++ b/apps/files_sharing/src/components/NewFileRequestDialog.vue
@@ -107,7 +107,7 @@
@click="onFinish">
+ {{ t('files_sharing', 'To upload files, you need to provide your name first.') }}
+
+ {{ t('files_sharing', '{ownerDisplayName} shared a folder with you.', { ownerDisplayName }) }}
+
+
+
+
- +
t('%s shared a folder with you.', [$_['shareOwner']])) ?>
+